Рекомендуется хранить веса в базе данных SQL?
приложение, над которым я работаю, должно хранить веса формата X pounds, y.y ounces
. База данных MySQL, но я предполагаю, что это DB agnostic.
Я могу придумать три способа сделать это:
- преобразуйте вес в десятичные фунты и храните в одном поле. (5 кг 6.2 ОЗ = 5.33671875 кг)
- преобразуйте вес в десятичные унции и храните в одном поле. (5 фунтов 6,2 унции = 86,2 унции)
- храните часть фунтов как целое число и унции в виде десятичной дроби, в двух полях.
Я думаю, что #1 не такая хорошая идея, так как десятичные фунты будут производить числа произвольной точности, которые нужно будет хранить как поплавок, что может привести к неточностям, которые присущи числам с плавающей запятой.
есть ли веская причина выбрать #2 вместо #3 или наоборот?
3 ответов
TL; DR
Выберите вариант #1 или Вариант #2-между ними нет разницы. Не используйте вариант №3, потому что с ним неудобно работать.
вы утверждаете, что есть присущие неточности в числах с плавающей запятой. Я думаю, что это заслуживает объяснения.
при принятии решения о система счисления для представления числа (будь то на листе бумаги, в компьютерной схеме или в другом месте), есть два отдельные вопросы для рассмотрения:
его основа; и
его .
выберите базу, любую базу...
ограниченный конечным пространством, нельзя представлять произвольный член бесконечное множество. например: независимо от того, сколько бумаги вы покупаете или насколько мал ваш почерк, он всегда можно найти целое число, которое не будет вписываться в заданное пространство (вы можете просто добавлять дополнительные цифры, пока бумага не закончится). Итак, с чисел, мы обычно ограничиваем наше конечное пространство, представляя только те, которые попадают в определенный интервал-например, если у нас есть пространство для трех цифр, мы могли бы ограничить себя интервалом [-999,999]
.
непустой интервал содержит бесконечный набор действительное число. другими словами, независимо от того, какой интервал один берет на себя реальные цифры-это [-999,999]
, [0,1]
, [0.000001,0.000002]
или что-нибудь еще-есть еще бесконечное множество чисел в пределах этого интервала! Поэтому произвольные действительные числа должны всегда быть "округленным" до чего-то, что может быть представлено в конечная пространство.
множество вещественных чисел, которые могут быть представлены в конечном пространстве, зависит от числа система, которая используется. в нашей (привычной) позиционные база-10 система, конечное пространство будет достаточно для половины (0.510
), но не на одну треть (0.33333…10
); напротив, в (менее знакомом) позиционном база-9 система, это наоборот (те же самые номера соответственно 0.44444…9
и 0.39
). иррациональные числа всегда требуют бесконечного пространства в стандартных позиционных систем. Следствием всего этого является то, что некоторые числа, которые можно представить, используя только небольшое пространство в позиционной базе-10 (и, следовательно,появляется быть очень " круглым "для нас, людей) на самом деле потребует бесконечных двоичных схем для хранения (и поэтому не кажется очень" круглым " для наших цифровых друзей)!
мы не можем сделать лучше для постоянного помногу. в конечном счете такие величины должны использовать конечное представление в некоторые система счисления: произвольно, будет ли эта система легко на компьютерных схемах, на человеческих пальцах, на чем-то еще или ни на чем вообще-какая бы система ни использовалась, значение должны округлить и поэтому это всегда приводит к "ошибке представления".
другими словами, даже если у вас есть совершенно точный измерительный прибор (что физически невозможно), то любое измерение он сообщает будут уже округлены к числу, которое случайно помещается на его дисплее (в любой базе, которую он использует-обычно десятичное, по понятным причинам). Таким образом," 86.2 oz "никогда на самом деле"86.2 ОЗ " но скорее представление "что-то между 86.1500000... oz и 86.2499999... ОЗ!--58-->". (На самом деле, поскольку на самом деле инструмент несовершенен, все, что мы можем сказать, это то, что мы есть некоторые степень доверия что фактическое значение попадает в этот интервал, но это, безусловно, вылетающих из точки здесь).
но мы можем сделать лучше для дискретных величин. Такие значения не являются "произвольными вещественными числами" и поэтому ни одно из вышеперечисленных не относится к ним: их можно представить ровно в системе счисления, в которой они были определены-и действительно,должно быть (как преобразование в другая числительная система и усечение до конечной длины приведет к округлению до неточного числа). Компьютеры могут (неэффективно) обрабатывать такие ситуации, представляя число в виде строки: например, рассмотрим ASCII или BCD кодировка.
применить формат...
поскольку это свойство (несколько произвольного) базиса числительной системы,кажется ли значение "круглым" или нет, не имеет никакого отношения к его точность. Это очень важное замечание, что противоречит интуиции многих людей (и именно поэтому я потратил так много времени, объясняя числовую основу выше).
точность вместо этого определяется сколько значащие цифры представлении. Нам нужен формат хранения, который способен записывать наши значения в по крайней мере как много значащих цифр as мы считаем их правильными. Принимая в качестве примера значения, которые мы считаем правильными, когда указано как 86.2
и 0.0000862
, два наиболее распространенных варианта:
-
фиксированная точка, где количество значимых цифр зависит от величины: например, в фиксированном 5-десятичном представлении наши значения будут храниться как
86.20000
и0.00009
(и поэтому имеют 7 и 1 значительные цифры точности соответственно.) В этом примере точность была потеряна в последнем значении (и действительно, нам не потребовалось бы намного больше, чтобы мы были полностью неспособны представлять что-нибудь значения); и прежнее значение хранится ложная точность, что является пустой тратой нашего конечного пространства (и действительно, не потребуется намного больше, чтобы значение стало настолько большим, что оно переполняет емкость хранилища).общий пример когда этот формат может быть подходящим для системы учета: валюта обычно должна отслеживаться копейки независимо от денежной суммы (поэтому для малых значений требуется меньшая точность, но для больших значений требуется большая точность). Как это бывает, валюта обычно также считается дискретной (копейки неделимы), поэтому это также хороший пример ситуации, когда конкретный базис (десятичный для большинства современных валют) желательно избегайте ошибок представления, описанных выше.
обычно реализуется хранение фиксированной точки, обрабатывая свои значения как коэффициенты над общим знаменателем и сохранение числителя в виде целого числа. В нашем примере общим знаменателем может быть 105, вместо
86.20000
и0.00009
один будет хранить целые числа8620000
и9
и помните, что они должны быть разделены100000
. -
с плавающей точкой, где количество значимых цифр постоянным, независимо от величины: например, в 5-значном десятичном представлении наши значения будут храниться как
86.200
и0.000086200
(и, по определению, имеют 5 значительных цифр точности оба раза). В этом примере оба значения были сохранены без потери точности; и оба они также имеют одинаковую сумму ложной точности, что менее расточительно (и поэтому мы можем использовать наше конечное пространство для представления гораздо большего диапазона значений-как больших, так и малых).общий пример того, когда этот формат может быть подходящим для записи любые измерения реального мира: точность измерительных приборов (которые все страдают от обоих систематически и случайные ошибки) довольно постоянны независимо от масштаба Итак, учитывая достаточные значимые цифры (обычно около 3 или 4 цифр), абсолютно никакая точность не теряется даже если изменение базы привело к округлению до другого числа.
обычно реализуется хранение с плавающей запятой, обрабатывая свои значения как целое significands с целыми показателями. В нашем примере значение может быть
86200
для обоих значений, после чего показатель (base-10) будет равен-4
и-9
соответственно.но насколько точны форматы хранения с плавающей запятой используется нашими компьютерами?
An IEEE754 одиночная точность (binary32) с плавающей запятой число имеет 24 бита, или
log10(224)
(более 7) цифр, значения-т. е. он имеет допуск менее±0.000006%
. Другими словами, это точнее, чем сказать "86.20000
".в IEEE754 двойная точность (binary64) с плавающей запятой число имеет 53 бита, или
log10(253)
(почти 16) цифр, значения-т. е. он имеет допуск чуть более±0.00000000000001%
. Другими словами, это точнее, чем сказать "86.2000000000000
".
самое главное, чтобы понять, что эти форматы, соответственно, более десять тысяч и один триллион раз точнее чем говорить "86.2" - даже если их представления в двоичном формате округляются до чисел, которые появляется менее "точный" в десятичном формате (подробнее об этом в ближайшее время)!
обратите внимание также, что и основные и форматы с плавающей запятой приведут к потере точности, когда значение известно более точно, чем поддерживает формат. такие ошибки округления может распространяться в арифметических операциях для получения явно ошибочных результатов (что, несомненно, объясняет вашу ссылку на "присущие неточности" чисел с плавающей запятой): например, 1⁄3 × 3000
в 5-месте фиксированная точка даст 999.99000
, а не 1000.00000
; и 10⁄81 − 3⁄25
в 5-значной цифре плавающая точка даст 0.0034600
, а не 0.0034568
.
поле численный анализ посвящен пониманию этих эффектов, но важно понимать это любой полезная система (даже выполнение расчетов в вашей голове) уязвима для таких проблем, потому что ни один метод расчета, который гарантированно завершится, не может предложить бесконечную точность: рассмотрим, например, как вычислить площадь окружности-обязательно будет потеря точности в значении, используемом для π, которое будет распространяться на результат.
вывод
реальный мир измерения должны использовать двоичную плавающую точку: это быстро, компактно, чрезвычайно точно и не хуже, чем что-либо еще (включая десятичную версию, с которой вы начали). С типы данных с плавающей запятой MySQL являются IEEE754, это именно то, что они предлагают.
валюта приложения должны использовать динариев фиксированной точки: пока оно медленн и расточительствует память, оно обеспечивает оба что значения не округлены к неточным количествам и что Пенни не теряются на крупные денежные суммы. С типы данных с фиксированной точкой MySQL являются строками с кодировкой BCD, это именно то, что они предлагают.
наконец, имейте в виду, что большинство языков программирования представляют дробные значения, используя двоичную плавающую точку типы: поэтому, даже если ваша база данных хранит значения в другом формате, они, вероятно, будут преобразованы (со всеми вытекающими отсюда проблемами) в интерфейсе с вашим код приложения.
какой вариант лучше в данном случае?
надеюсь, я убедил вас, что ваши ценности могут безопасно (и должны) храниться в типах с плавающей запятой, не беспокоясь о каких-либо"неточностях"? Помните, что они больше точное, чем ваше хлипкое 3-значное десятичное представление когда-либо было: вам просто нужно игнорировать ложную точность (но нужно всегда сделайте это в любом случае, даже если используете фиксированную точку десятичный формат.)
что касается вашего вопроса: выберите вариант 1 или 2 над вариантом 3-это упрощает сравнение (например, чтобы найти максимальную массу, можно просто использовать MAX(mass)
, тогда как для эффективного выполнения этого в двух столбцах потребуется некоторая вложенность).
в целом, между этими двумя вариантами не имеет большого значения, какой из них выбрать-числа с плавающей запятой хранятся с постоянным количеством значимых битов независимо от их масштаба (действительно, это может быть что некоторые значения округлены до чисел, которые находятся ближе к их исходное десятичное представление, используя вариант 1 и одновременно другим округляются до чисел, которые находятся ближе к их исходной десятичной записи через Вариант 2: это просто зависит от того, как каждое конкретное значение может быть представлено в двоичной системе).
на этой случай, потому что случается что 16 унций к 1 фунту (и 16 Сила 2), относительная разница между исходными десятичными значениями и числами, сохраненными с использованием двух подходов, составляет одинаковых:
-
5.387510
(не5.3367187510
как указано в вашем вопросе) будет храниться в binary32 float как101.0110001100110011001102
(т. е.5.3874998092651367187510
): это0.0000036%
от исходного значения (но, как обсуждалось выше, "исходное значение" уже было довольно паршивым представлением физической величины, которую оно представляет).зная что binary32 float хранит только 7 десятичных цифр точности, наш компилятор знает для некоторых что все, начиная с 8-й цифры и далее наверняка ложная точность и поэтому должны игнорировать case-таким образом, при условии, что наше входное значение не требует большей точности, чем это (и если это так, binary32 был явно неправильным выбором формата), это гарантии возврат к десятичному значению, которое выглядит так же кругло, как и то, с чего мы начали:
5.38750010
. Тем не менее, мы должны действительно применить знание предметной области в этот момент (как и в любом формате хранения), чтобы отбросить любую дополнительную ложную точность, которая может существовать, например, эти два конечных нуля. 86.210
будет храниться в binary32 float как1010110.001100110011001102
(т. е.86.199996948242187510
): это тоже0.0000036%
от первоначальной стоимости. Как и раньше, мы игнорируем false точность.
обратите внимание, как двоичные представления чисел идентичны, за исключением размещения точки радикса (это четыре бита друг от друга):
101.0110 00110011001100110 101 0110.00110011001100110
это потому, что 5.3875 × 24 = 86.2.
в стороне: будучи европейским (хотя и британским), я также испытываю сильное отвращение к имперским единицам измерения-обработка значений разных масштабов просто так грязный. Я почти наверняка буду хранить массы в Си (например, килограммы или граммы), а затем выполните преобразования в имперские единицы, как требуется в внешний вид Мои приложения. Плюс жестко придерживаясь единиц СИ может однажды спасти вас от потерять $125 млн.
Я бы соблазнился хранить его в метрической единице, поскольку они, как правило, простые десятичные дроби, а не сложные значения, такие как фунты и унции. Таким образом, вы можете просто сохранить одно значение (т. е. 103.25 кг), а не эквивалент фунтов–унций, и легче выполнять преобразования.
Это то, с чем я имел дело в прошлом. Я много работаю над сайтами pro wrestling и mixed martial arts (MMA), где нужно записывать высоту и вес бойцов. Они склонны быть отображается в футах и дюймах, фунтах и унциях, но я по-прежнему храню значения в их сантиметрах и килограммах эквивалентов, а затем сделать преобразование при отображении на сайте.
во-первых, я не знал о том, как числа с плавающей запятой были неточными - к счастью, поиск последнего помогает мне понять:Примеры Неточностей С Плавающей Запятой
Я бы полностью согласился с @eggyal-сохранить данные в одном формате в одном столбце. Это позволяет вам подвергнуть его воздействию приложения и позволить приложению иметь дело с его презентацией - будь то в фунтах/унциях, округленных фунтах, что угодно.
база данных должна хранить необработанные данные, а макет определяется уровнем презентации.