Mysql
 sql >> база данни >  >> RDS >> Mysql

Най-добра практика за съхраняване на тегла в SQL база данни?

Вие твърдите, че има присъщи неточности в числата с плаваща запетая. Мисля, че това заслужава първо да бъде проучено.

Когато вземете решение за бройна система за представяне на число (независимо дали на лист хартия, в компютърна схема или другаде), има две отделни въпроси, които трябва да се вземат предвид:

  1. неговата основа; и

  2. неговия формат .

Изберете база, произволна база...

Ограничено от ограничено пространство, човек не може да представлява произволен член на безкраен набор . Например:без значение колко хартия купувате или колко малък е вашият почерк, винаги ще бъде възможно да намерите цяло число, което няма да се побере в даденото пространство (можете просто да продължите да добавяте допълнителни цифри, докато хартията свърши). И така, с цели числа , обикновено ограничаваме нашето крайно пространство до представяне само на онези, които попадат в определен интервал – напр. ако имаме място за положителен/отрицателен знак и три цифри, може да се ограничим до интервала [-999,+999] .

Всяка непразен интервал съдържа безкраен набор от реални числа С други думи, независимо на какъв интервал се поемат реалните числа — било то [-999,+999] , [0,1] , [0.000001,0.000002] или нещо друго — все още има безкраен набор от реални числа в рамките на този интервал (трябва само да продължите да добавяте (не-нула) дробни цифри)! Следователно произволните реални числа трябва винаги да бъде "закръглено" до нещо, което може да бъдат представени в крайно пространство.

Наборът от реални числа, които могат да бъдат представени в крайно пространство, зависи от използваната бройна система. В нашия (познат) позиционен base-10 система, ограниченото пространство ще бъде достатъчно за една половина (0.510 ), но не и за една трета (0.33333…10 ); за разлика от това, в (по-малко познатата) позиционна base-9 система е обратното (същите тези числа са съответно 0.39 ). Последствието от всичко това е, че някои числа, които могат да бъдат представени, използвайки само малко пространство в позиционна основа-10 (и следователно появяват се да бъде много "кръгла" към нас хората), напр. една десета, всъщност ще изисква безкрайни двоични вериги да се съхраняват прецизно (и следователно не изглеждат много "кръгли" за нашите цифрови приятели)! Трябва да се отбележи, че тъй като 2 е коефициент 10, същото не е вярно и в обратната посока:всяко число, което може да бъде представено с краен двоичен, може също да бъде представено с краен десетичен знак.

Не можем да направим нищо по-добро за непрекъснати количества. В крайна сметка такива количества трябва да използват крайно представяне в някои числова система:произволно е дали тази система е лесна за компютърни вериги, за човешки пръсти, за нещо друго или за нищо – каквато и система да се използва, стойността трябва бъде закръглена и следователно винаги води до "грешка при представяне".

С други думи, дори ако човек има идеално точен измервателен инструмент (което е физически невъзможно), тогава всяко измерване, което отчита ще е вече закръглено до число, което случайно се побира на дисплея му (в каквато и основа да използва - обикновено десетично, по очевидни причини). Така че „86,2 oz“ всъщност никога не е „86,2 oz " а по-скоро представяне на "нещо между 86.1500000... oz и 86.2499999... oz ". (Всъщност, тъй като в действителност инструментът е несъвършен, всичко, което наистина можем да кажем е, че имаме някакъв степен на доверие че действителната стойност попада в този интервал — но това определено се отклонява донякъде от точката тук).

Но можем да се справим по-добре за дискретни количества . Такива стойности не са „произволни реални числа“ и следователно нищо от горното не се отнася за тях:те могат да бъдат представени точно в бройната система, в която са определени — и наистина, трябва да бъдат (тъй като преобразуването в друга числова система и съкращаването до крайна дължина би довело до закръгляване до неточно число). Компютрите могат (неефективно) да се справят с такива ситуации, като представят числото като низ:напр. помислете за ASCII или BCD кодиране.

Прилагане на формат...

Тъй като това е свойство на основата на числовата система (донякъде произволна), дали дадена стойност изглежда "кръгла" няма значение за нейната прецизност . Това е наистина важно наблюдение , което противоречи на интуицията на много хора (и това е причината да прекарах толкова много време в обяснение на числовата база по-горе).

Вместо това точността се определя от колко значими цифри представителство имат . Нуждаем се от формат за съхранение, който може да записва нашите стойности до поне толкова значими цифри, колкото ги считаме за правилни . Вземайки като пример стойности, които считаме за правилни, когато са посочени като 86.2 и 0.0000862 , двете най-често срещани опции са:

  • Фиксирана точка , където броят на значимите цифри зависи от големината :напр. при фиксирано представяне с 5 десетични запетаи, нашите стойности ще бъдат съхранени като 86.20000 и 0.00009 (и следователно имат съответно 7 и 1 значими цифри на точност). В този пример прецизността е загубена в последната стойност (и наистина, нямаше да ни е нужно много повече, за да сме напълно неспособни да представим нищо от значение); и предишната стойност се съхранява фалшива точност , което е загуба на нашето ограничено пространство (и наистина няма да е нужно много повече, за да стане стойността толкова голяма, че да превиши капацитета за съхранение).

    Често срещан пример за това кога този формат може да е подходящ е за счетоводна система:паричните суми обикновено трябва да се проследяват до пени независимо от тяхната величина (следователно се изисква по-малко прецизност за малки стойности и по-голяма точност за големи стойности). Както се случва, валутата обикновено също се счита за отделна (стотинките са неделими), така че това също е добър пример за ситуация, при която конкретна база (десетична за повечето съвременни валути) е желателна, за да се избегнат грешките при представяне, обсъдени по-горе.

  • Плававаща точка , където броят на значимите цифри е постоянен, независимо от големината :напр. в десетично представяне с 5 значими цифри, нашите стойности ще бъдат съхранени като 86.200 и 0.000086200 (и по дефиниция имат 5 значими цифри на точност и двата пъти). В този пример и двете стойности са били съхранени без загуба на прецизност; и двете също имат еднакво количество с фалшива прецизност, което е по-малко разточително (и следователно можем да използваме нашето ограничено пространство, за да представим далеч по-голям диапазон от стойности – както големи, така и малки).

    Често срещан пример за това кога този формат може да е подходящ е за записване на всички измервания в реалния свят :прецизността на измервателните уреди (които всички страдат и от систематично и случаен грешки) е сравнително постоянен, независимо от мащаба, така че, като се имат предвид достатъчно значими цифри (обикновено около 3 или 4 цифри), не се губи абсолютно никаква прецизност дори ако промяната на основата доведе до закръгляване до различно число .

    Ноколко точни са форматите за съхранение с плаваща запетая използвани от нашите компютри?

    Най-важното нещо, което трябва да разберете, е, че тези формати са съответно над десет хиляди и над един трилион пътипо-точно отколкото да кажете „86.2“ — въпреки че точните преобразувания на двоичния код обратно в десетичен случай включват погрешна фалшива точност (която трябва да пренебрегнем:повече за това скоро)!

Забележете също, че и двете фиксирани и Форматите с плаваща запетая ще доведат до загуба на прецизност, когато стойността е известна по-точно, отколкото форматът поддържа. Такива грешки при закръгляването може да се разпространява в аритметични операции, за да даде очевидно грешни резултати (което без съмнение обяснява препратката ви към „присъщите неточности“ на числата с плаваща запетая):например 3 × 3000 в 5-местна фиксирана точка ще даде 999.99000 вместо 1000.00000; и 7 − ⁄50 в 5-значима цифра с плаваща запетая ще даде 0.0028600 вместо 0.0028571 .

Полето на числовия анализ е посветен на разбирането на тези ефекти, но е важно да се осъзнае, че всяко използваемата система (дори извършването на изчисления в главата ви) е уязвима към подобни проблеми, защото нито един метод на изчисление, който гарантирано ще приключи, не може да предложи безкрайна прецизност :помислете например как да изчислите площта на окръжност – непременно ще има загуба на прецизност в стойността, използвана за π, която ще се разпространи в резултата.

Заключение

  1. Измерванията в реалния свят трябва да използват двоична плаваща запетая :той е бърз, компактен, изключително прецизен и не по-лош от всичко друго (включително десетичната версия, от която сте започнали). Тъй като типовете данни с плаваща запетая на MySQL са IEEE754, точно това предлагат.

  2. Приложенията за валута трябва да използват фиксирана точка на денари :въпреки че е бавен и губи паметта, той гарантира, че стойностите не се закръгляват до неточни количества и че стотинките не се губят при големи парични суми. Тъй като типовете данни с фиксирана точка на MySQL са BCD-кодирани низове, точно това предлагат.

И накрая, имайте предвид, че езиците за програмиране обикновено представляват дробни стойности с помощта на двоична плаваща запетая типове:така че ако вашата база данни съхранява стойности в друг формат, трябва да внимавате как те се въвеждат във вашето приложение, в противен случай те могат да бъдат преобразувани (с всички произтичащи от това проблеми) в интерфейса.

Коя опция е най-добра в този случай?

Надявам се, че съм ви убедил, че вашите ценности могат безопасно (и трябва ) да се съхраняват във типове с плаваща запетая, без да се притеснявате твърде много за някакви "неточности"? Не забравяйте, че те са повече прецизно, отколкото вашето крехко 3-значимо десетично представяне някога:просто трябва да игнорирате фалшивата прецизност (но човек трябва винаги направете това все пак, дори ако използвате десетичен формат с фиксирана точка).

Що се отнася до въпроса ви:изберете или опция 1 или 2 пред опция 3 – това улеснява сравненията (например, за да намерите максималната маса, можете просто да използвате MAX(mass) , докато за да го направите ефективно в две колони ще е необходимо известно влагане).

Между тези две няма значение кой ще избере – числата с плаваща запетая се съхраняват с постоянен брой значими битове независимо от техния мащаб .

Освен това, докато в общия случай може да се случи някои стойности да се закръглят до двоични числа, които са по-близо до първоначалното им десетично представяне, като се използва опция 1, докато едновременно с това други се закръглят до двоични числа, които са по-близо до първоначалното им десетично представяне, като се използва опция 2, като скоро ще видим, че подобни грешки в представянето се проявяват само в рамките на фалшивата точност, която винаги трябва да се игнорира.

Въпреки това, в това случай, тъй като се случва, че има 16 унции към 1 паунд (а 16 е степен на 2), относителните разлики между оригиналните десетични стойности и съхранените двоични числа, използващи двата подхода, са идентични :

  1. 5.387510 (не 5.3367187510 както е посочено във вашия въпрос) ще се съхранява в binary32 float като 101.0110001100110011001102 (което е 5.3874998092651367187510 ):това е 0.0000036% от първоначалната стойност (но, както беше обсъдено по-горе, "оригиналната стойност" вече беше доста гадно представяне на физическото количество, което представлява).

    Знаейки, че binary32 float съхранява само 7 десетични цифри с точност, нашият компилатор знае сигурно че всичко от 8-та цифра нататък е определено фалшива точност и следователно трябва да се игнорира във всеки случай – по този начин, при условие, че нашата входна стойност не изисква по-голяма точност от това (а ако е така, binary32 очевидно е грешен избор на формат), това гарантира връщане към десетична стойност, която изглежда също толкова кръгла като тази, от която започнахме:5.38750010 . Въпреки това, наистина трябва да приложим знание за домейна в този момент (както би трябвало с всеки формат за съхранение), за да отхвърлим всяка по-нататъшна фалшива точност, която може да съществува, като тези две крайни нули.

  2. 86.210 ще се съхранява в binary32 float като 1010110.001100110011001102 (което е 86.199996948242187510 ):това също е 0.0000036% от първоначалната стойност. Както и преди, след това игнорираме фалшивата точност, за да се върнем към първоначалния си вход.

Забележете как двоичните представяния на числата са идентични, с изключение на разположението на основната точка (което е на четири бита един от друг):

101.0110 00110011001100110
101 0110.00110011001100110

Това е така, защото 5,3875 × 2 =86,2.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Код за конфигурация на CodeIgniter и поддръжка на UTF-8

  2. Грешка при вмъкване на MySQL:ER_BAD_FIELD_ERROR:Неизвестна колона „2525“ в „списък с полета“

  3. PHP - извличане на подготвен stmt в клас:Класът за фатална грешка не е намерен

  4. Създаване на kml файл от mysql база данни с php

  5. Как да задам избрания елемент в падащо меню