Используйте Float или Decimal для учета суммы доллара приложения?
мы переписываем нашу устаревшую систему учета в VB.NET и SQL Server. Мы привезли в новую команду .Сеть/ SQL-программисты, чтобы сделать исправления. Большая часть системы уже завершена с долларовыми суммами с использованием поплавков. Устаревший системный язык, в котором я запрограммирован, не имел поплавка, поэтому я, вероятно, использовал бы десятичную дробь.
какова ваша рекомендация?
должен ли тип данных Float или Decimal использоваться для сумм в долларах?
какие плюсы и минусы для обоих?
один Con, упомянутый в нашем ежедневном scrum, заключался в том, что вам нужно быть осторожным при расчете суммы, которая возвращает результат, превышающий две десятичные позиции. Похоже, вам придется округлить сумму до двух десятичных позиций.
другой Con-это все дисплеи и печатные суммы должны иметь оператор формата, который показывает две десятичные позиции. Я заметил несколько раз, где это не было сделано, и суммы не выглядеть правильно. (т. е. 10.2 или 10.2546)
A pro-это поплавок, занимающий только 8 байт на диске, где десятичное число займет 9 байт (десятичное число 12,2)
24 ответов:
следует ли использовать тип данных Float или Decimal для сумм в долларах?
ответ прост. Никогда не плавает. никогда !
поплавки были в соответствии с IEEE 754 всегда двоичный, только новый стандарт IEEE 754R определенные десятичные форматы. Многие дробные двоичные части никогда не могут равняться точному десятичному числу представление. Любое двоичное число может быть записано как m / 2^n (m, n положительных целых чисел), любое десятичное число как m / (2^n*5^n). Поскольку двоичные файлы не имеют простого множителя 5, все двоичные числа могут быть точно представлены десятичными числами, но не наоборот.
0.3 = 3/(2^1 * 5^1) = 0.3
0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]
1/4 1/8 1/16 1/32таким образом, вы в конечном итоге с числом выше или ниже, чем заданное десятичное число. Всегда.
почему это важно ? Округление. Нормальное округление означает 0..4 вниз, 5..9 вверх. Так это тут имеет значение, если результат либо 0.049999999999.... или 0.0500000000... Вы можете знать, что это означает 5 центов, но компьютер этого не знает и округляет 0.4999... вниз (неправильно) и 0.5000... вверх (справа). Учитывая, что результат вычислений с плавающей запятой всегда содержит небольшие члены ошибок, решение является чистой удачей. Это становится безнадежным, если вы хотите десятичное округление до четного работа с двоичными числами.
при своем мнении ? Вы настаиваете, что в вашей учетной системе все в порядке? Активы а обязательства равны? Хорошо, тогда возьмите каждое из заданных отформатированных чисел каждой записи, проанализируйте их и суммируйте их с независимой десятичной системой! Сравните это с отформатированной суммой. Ой, тут что-то не так, не так ли ?
для этого расчета требовалась предельная точность и точность (мы использовали Oracle FLOAT), чтобы мы могли записать начисление "миллиардной части Пенни".
не помогает против этой ошибки. Потому что все люди автоматически предположим, что компьютер суммирует правильно, практически никто не проверяет самостоятельно.
эта фотография отвечает:
Это другая ситуация: человек из Нортгемптона получил письмо о том, что его дом будет конфискован, если он не заплатит ноль долларов и ноль центов!
сначала вы должны прочитать это Что Каждый Компьютерный Ученый Должен Знать Об Арифметике С Плавающей Запятой. Тогда вы действительно должны рассмотреть возможность использования некоторого типа фиксированная точка / произвольное число точности пакет (например, java BigNum, Python decimal module) в противном случае вы будете в мире боли. Затем выясните, достаточно ли использовать собственный десятичный тип SQL.
поплавки / двойники существуют(ed), чтобы выставить быстрый x87 fp, который теперь в значительной степени устарел. Не используйте их, если вы заботитесь о точности вычислений и/или не полностью компенсировать их ограничения.
в качестве дополнительного предупреждения SQL Server и платформа .Net framework используют другой алгоритм округления по умолчанию. Убедитесь, что вы проверили параметр MidPointRounding в математике.Раунд.)( .Net framework по умолчанию использует алгоритм банкиров, а SQL Server использует симметричное алгоритмическое округление. Проверьте статью Википедии здесь
спросите своих бухгалтеров! Они будут хмуриться на вас за использование поплавка. Как и некоторые из опубликованных ранее, используйте float только если вы не заботитесь о точности. Хотя я всегда был бы против, когда речь заходит о деньгах.
в бухгалтерском программном обеспечении не допускается поплавок. Использовать десятичное число с 4 знаками после запятой.
плавающей точки имеют неожиданные иррациональные числа.
например, вы не можете хранить 1/3 в виде десятичной дроби, это будет 0.3333333333... (и так далее)
поплавки фактически хранятся в виде двоичного значения и степени 2 экспоненты.
поэтому 1.5 хранится как 3 x 2 к -1 (или 3/2)
используя эти показатели базы-2, Создайте некоторые нечетные иррациональные числа, например:
преобразовать 1.1 к float, а затем преобразовать его обратно, ваш результат будет что-то вроде: 1.09999999999989
это потому, что двоичное представление 1.1 На самом деле 154811237190861 x 2^-47, больше, чем двойной может обрабатывать.
подробнее об этом вопросе на мой блог, но, в основном, для хранения, вам лучше с запятой.
на Microsoft SQL server у вас есть
moneyтип данных-это обычно лучше всего подходит для финансового хранения. Это с точностью до 4 десятичных разрядов.для расчетов у вас есть еще одна проблема - неточность-это крошечная часть, но поместите ее в функцию мощности, и она быстро станет значительной.
однако десятичные числа не очень хороши для любой математики - например, нет собственной поддержки десятичных степеней.
использовать SQL server decimal тип.
Не используйте деньги или float.
деньги используют 4 десятичных знака, быстрее, чем с помощью decimal но страдает от некоторых очевидных и некоторых не столь очевидных проблем с округлением (см. эту проблему подключения)
немного фона здесь....
никакая система счисления не может обрабатывать все реальные числа точно. Все они имеют свои ограничения, и это включает в себя как стандарт IEEE с плавающей запятой, так и знак десятичной дроби. Плавающая точка IEEE более точна на бит, но это не имеет значения здесь.
финансовые цифры основаны на веках бумажно-перьевой практики, с соответствующими конвенциями. Они достаточно точны, но, что более важно, они непередаваемы. Два бухгалтеры, работающие с различными цифрами и ставками, должны придумать один и тот же номер. Любое несоответствие - это место для мошенничества.
поплавки не являются точными представлениями, возможны проблемы с точностью, например, при добавлении очень больших и очень малых значений. Вот почему десятичные типы рекомендуются для валюты, хотя проблема точности может быть достаточно редкой.
чтобы уточнить, тип decimal 12,2 будет хранить эти 14 цифр точно, тогда как float не будет, поскольку он использует двоичное представление внутри. Например, 0.01 не может быть точно представлено числом с плавающей запятой - самое близкое представление на самом деле 0.0099999998
для банковской системы, которую я помогал развивать, я отвечал за часть системы "начисление процентов". Каждый день мой код подсчитывал, сколько процентов было начислено (заработано) на балансе в тот день.
для этого расчета требовалась предельная точность и точность (мы использовали поплавок Oracle), чтобы мы могли записать начисление "миллиардной части Пенни".
когда дело дошло до "капитализации" процентов (т. е. выплата процентов обратно на ваш счет) сумма была округлена до копейки. Тип данных для остатков по счету - два знака после запятой. (На самом деле это было сложнее, поскольку это была мультивалютная система, которая могла работать во многих десятичных знаках-но мы всегда округляли до "копейки" этой валюты). Да-там, где" доли " потерь и прибылей, но когда цифры компьютеров были актуализированы (деньги выплачены или выплачены), это всегда были реальные денежные ценности.
это удовлетворило бухгалтеров, аудиторов и тестеры.
Итак, проверьте с вашими клиентами. Они расскажут вам свои банковские/бухгалтерские правила и практику.
еще одна вещь, которую вы должны знать в системах бухгалтерского учета, заключается в том, что никто не должен иметь прямого доступа к таблицам. Это означает, что весь доступ к системе учета должен осуществляться через хранимые процедуры. Это предотвращает мошенничество не только SQL-инъекциям. Внутренний пользователь, который хочет совершить мошенничество, не должен иметь возможности напрямую изменять данные в таблицах базы данных. Это критический внутренний контроль в вашей системе. Вы действительно хотите, чтобы какой-то недовольный сотрудник пошел в бэкэнд вашей базы данных и начать записывать их проверки? Или скрыть, что они одобрили расходы для несанкционированного поставщика, когда у них нет полномочий на утверждение? Только два человека во всей вашей организации должны иметь прямой доступ к данным в вашей финансовой базе данных, вашей dba и его резервной копии. Если у вас есть много данных, только два из них должны иметь этот доступ.
я упоминаю об этом, потому что если ваши программисты использовали float в системе бухгалтерского учета, вероятно, они совершенно не знакомы с идея внутреннего контроля так и не была учтена ими в своих программных усилиях.
даже лучше, чем использовать десятичные числа, использует просто старые целые числа (или, может быть, какой-то bigint). Таким образом, вы всегда имеете максимально возможную точность, но точность может быть указана. Например, количество
100может означать1.00, который имеет следующий формат:int cents = num % 100; int dollars = (num - cents) / 100; printf("%d.%02d", dollars, cents);Если вы хотите иметь больше точности, вы можете изменить 100 на большее значение, например: 10 ^ n, где n-количество десятичных знаков.
вы всегда можете написать что-то вроде типа денег для. Net.
взгляните на эту статью: тип денег для CLR - автор сделал отличную работу на мой взгляд.
Я использовал тип денег SQL для хранения денежных значений. Недавно мне пришлось работать с несколькими платежными системами в интернете и заметил, что некоторые из них используют целые числа для хранения денежных значений. В моих текущих и новых проектах я начал использовать целые числа, и я довольно доволен этим решением.
из 100 дробей n / 100, где n-натуральное число, такое что 0
#include <stdio.h> int main() { printf("Mapping 100 numbers between 0 and 1 "); printf("to their hexadecimal exponential form (HEF).\n"); printf("Most of them do not equal their HEFs. That means "); printf("that their representations as floats "); printf("differ from their actual values.\n"); double f = 0.01; int i; for (i = 0; i < 100; i++) { printf("%1.2f -> %a\n",f*i,f*i); } printf("Printing 128 'float-compatible' numbers "); printf("together with their HEFs for comparison.\n"); f = 0x1p-7; // ==0.0071825 for (i = 0; i < 0x80; i++) { printf("%1.7f -> %a\n",f*i,f*i); } return 0; }
рассматривали ли вы возможность использования типа money-data для хранения долларовых сумм?
Что касается Con, что decimal занимает еще один байт, я бы сказал, что это не волнует. В 1 млн строк вы будете использовать только еще 1 МБ и хранение очень дешево в эти дни.
Что бы вы ни делали, вы должны быть осторожны с ошибками округления. Вычисление с использованием большей степени точности, чем вы показываете В.
вы, вероятно, захотите использовать некоторую форму представления фиксированной точки для значений валюты. Вы также захотите исследовать округление банкира (также известное как "круглая половина даже".) Это позволяет избежать предвзятости, которые существуют в обычном методе "round half up".
ваши бухгалтеры захотят контролировать, как вы округляете. Использование float означает, что вы будете постоянно округлять, обычно с помощью оператора типа FORMAT (), что не так, как вы хотите это сделать (вместо этого используйте пол / потолок).
У вас есть типы данных валюты (money, smallmoney), которые следует использовать вместо float или real. Хранение decimal (12,2) устранит ваши округления, но также устранит их во время промежуточных шагов - что на самом деле не то, что вы хотите вообще финансовое приложение.
числа с плавающей точкой только представляют собой числа, которые являются суммой отрицательных кратных базы-для двоичной плавающей запятой, конечно, это два.
есть только четыре десятичные дроби представимы точно в двоичной форме с плавающей точкой: 0, 0.25, 0.5 и 0.75. Все остальное-это приближение, точно так же, как 0.3333... приблизительно на 1/3 в десятичной арифметики.
плавающая точка является хорошим выбором для вычислений, где масштаб результата-вот что важно. Это плохой выбор, когда вы пытаетесь быть точным до некоторого количества десятичных знаков.
Это отличная статья с описанием когда использовать float и decimal. Float хранит приблизительное значение, а decimal - точное.
вот интересный пример, который показывает, что как float, так и decimal способны терять точность. При добавлении числа, которое не является целым числом, а затем вычитание то же самое число float приводит к потере точности, в то время как decimal не делает:
DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; SET @Float1 = 54; SET @Float2 = 3.1; SET @Float3 = 0 + @Float1 + @Float2; SELECT @Float3 - @Float1 - @Float2 AS "Should be 0"; Should be 0 ---------------------- 1.13797860024079E-15при умножении нецелого числа и делении на это же число десятичные числа теряют точность, а плавающие-нет.
DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); SET @Fixed1 = 54; SET @Fixed2 = 0.03; SET @Fixed3 = 1 * @Fixed1 / @Fixed2; SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1"; Should be 1 --------------------------------------- 0.99999999999999900


Comments