Использовать сумму пополнения или десятичную сумму для поступления в доллары США?
Мы переписываем нашу устаревшую систему учета в VB.NET и SQL Server. Мы внесли новую команду .NET/SQL Programmers, чтобы сделать переписывание. Большая часть системы уже заполнена суммами доллара с использованием Floats. Унаследованный системный язык, который я запрограммировал, не имел Float, поэтому я, вероятно, использовал бы Decimal.
Какова ваша рекомендация?
Должны ли использоваться типы данных Float или Decimal для сумм в долларах?
Каковы некоторые из плюсов и минусов для?
One Con, упомянутый в нашей ежедневной схватке, должен был быть осторожным, когда вы вычисляете сумму, которая возвращает результат, который превышает две десятичные позиции. Похоже, вам придется округлить сумму до двух десятичных позиций.
Другой Con - все дисплеи, а напечатанные суммы должны иметь формат Statement, который показывает две десятичные позиции. Я заметил несколько раз, когда это не было сделано, и суммы не выглядели правильными. (то есть 10,2 или 10,2546).
A pro - это Float занимает до 8 байтов на диске, где Decimal будет занимать 9 байтов (Decimal 12,2)
Ответы
Ответ 1
Следует ли использовать тип данных Float или Decimal для сумм в долларах?
Ответ прост. Никогда не плавает. НИКОГДА !
В соответствии с IEEE 754 значения с плавающей точкой всегда были двоичными, только новый стандарт IEEE 754R определял десятичные форматы. Многие дробные двоичные части никогда не могут быть равны точному десятичному представлению.
Любое двоичное число может быть записано как m/2^n
(m
, n
натуральных чисел), любое десятичное число как m/(2^n*5^n)
.
Поскольку в двоичных файлах нет простого factor 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), чтобы мы могли зафиксировать "миллиардную копейку", которая была начислена.
Не помогает против этой ошибки. Поскольку все люди автоматически предполагают, что компьютер суммирует правильно, практически никто не проверяет самостоятельно.
Ответ 2
Эта фотография отвечает:
Это еще одна ситуация: человек из Нортгемптона получил письмо с выражением о том, что его дом будет арестован, если он не заплатит нулевые доллары и нулевые центы!
Ответ 3
Сначала вы должны прочитать это Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой. Тогда вам стоит подумать об использовании некоторого типа пакета номер с фиксированной или произвольной точностью (например, java BigNum, десятичный модуль python), иначе вы будете в мире обид. Затем выясните, достаточно ли использовать собственный десятичный тип SQL.
Floats/doubles существуют (ed), чтобы выставить быстрый x87 fp, который теперь довольно устарел. Не используйте их, если вы заботитесь о точности вычислений и/или не полностью компенсируете их ограничения.
Ответ 4
Также как дополнительное предупреждение, SQL Server и .Net используют другой алгоритм по умолчанию для округления. Убедитесь, что вы проверили параметр MidPointRounding в Math.Round()..Net использует алгоритм Bankers по умолчанию, а SQL Server использует симметричное алгоритмическое округление. Просмотрите статью Википедии здесь
Ответ 5
Спросите своих бухгалтеров! Они будут хмуриться вам за использование float. Как и раньше, используйте float ТОЛЬКО, если вам не нужна точность. Хотя я всегда буду против этого, когда дело доходит до денег.
В бухгалтерском программном обеспечении НЕ допускается поплавок. Используйте десятичный знак с четырьмя десятичными знаками.
Ответ 6
Плавающие точки имеют неожиданные иррациональные числа.
Например, вы не можете хранить 1/3 как десятичную, это будет 0.3333333333... (и т.д.)
Поплавки фактически хранятся как двоичное значение и степень 2 экспонента.
Таким образом, 1.5 сохраняется как 3 x 2 до -1 (или 3/2)
Использование этих базовых показателей 2 создает некоторые нечетные иррациональные числа, например:
Преобразовать 1.1 в float и затем снова преобразовать его, ваш результат будет примерно таким: 1.0999999999989
Это связано с тем, что двоичное представление 1.1 на самом деле составляет 154811237190861 x 2 ^ -47, более чем двойное может обрабатывать.
Подробнее об этой проблеме на в моем блоге, но в основном для хранения вам лучше с десятичными знаками.
На сервере Microsoft SQL у вас есть тип данных money
- это обычно лучше всего подходит для финансового хранилища. Точность до 4 десятичных позиций.
Для расчетов у вас больше проблемы - неточность - это крошечная фракция, но она попадает в силовую функцию, и она быстро становится значимой.
Однако десятичные знаки не очень хороши для любой математики - нет встроенной поддержки десятичных степеней, например.
Ответ 7
Используйте тип SQL Server десятичный.
Не используйте деньги или плавайте.
деньги используют 4 десятичных разряда, быстрее, чем использование десятичной НО страдает от некоторых очевидных и некоторых не столь очевидных проблем с округлением (см. эту проблему подключения)
Ответ 8
Я бы рекомендовал использовать 64-битные целые числа, которые хранят все в центах.
Ответ 9
Немного фона здесь....
Никакая система цифр не может точно обрабатывать все реальные числа. Все они имеют свои ограничения, и это включает в себя как стандартную плавающую точку IEEE, так и подписанную десятичную. Плавающая точка IEEE более точная для каждого используемого бита, но это не имеет значения здесь.
Финансовые показатели основаны на многовековой практике бумажной печати с соответствующими соглашениями. Они достаточно точны, но, что более важно, они воспроизводимы. Два бухгалтера, работающие с различными номерами и ставками, должны иметь одинаковое количество. Любая комната для расхождения - это место для мошенничества.
Поэтому для финансовых расчетов правильный ответ - это тот, который дает тот же ответ, что и CPA, который хорошо разбирается в арифметике. Это десятичная арифметика, а не плавающая точка IEEE.
Ответ 10
Поплавки не являются точными представлениями, возможны точные проблемы, например, при добавлении очень больших и очень малых значений. Именно поэтому десятичные типы рекомендуются для валюты, даже если точность вопроса может быть достаточно редкой.
Чтобы уточнить, десятичный тип 12,2 будет хранить эти 14 цифр точно, тогда как float не будет, поскольку он использует двоичное представление внутри. Например, 0.01 не может быть точно представлено числом с плавающей запятой - самое близкое представление на самом деле равно 0,0099999998
Ответ 11
Единственная причина использовать Float для денег - это то, что вам не нужны точные ответы.
Ответ 12
Для банковской системы, которую я помогал в разработке, я отвечал за часть "начисления процентов" в системе. Каждый день мой код рассчитывал, сколько процентов было начислено (заработано) на баланс в тот день.
Для этого расчета требовалась высокая точность и точность (мы использовали Oracle FLOAT), чтобы мы могли записать "миллиардную долю пенни".
Когда дело дошло до "капитализации" процентов (т.е. выплаты процентов обратно на ваш счет), сумма была округлена до копейки. Тип данных для остатков на счетах составлял два знака после запятой. (На самом деле это было сложнее, поскольку это была мультивалютная система, которая могла работать во многих десятичных разрядах, но мы всегда округлились до "копейки" этой валюты). Да, там, где "фракции" потерь и выигрышей, но когда показатели компьютеров были актуализированы (деньги выплачивались или оплачивались), это всегда были реальные денежные значения.
Это удовлетворило бухгалтеров, аудиторов и тестировщиков.
Итак, обратитесь к своим клиентам. Они расскажут вам о правилах и практике их банковского дела/учета.
Ответ 13
Еще одна вещь, о которой вам следует знать в системах учета, - это то, что никто не должен иметь прямой доступ к таблицам. Это означает, что доступ к системе учета должен осуществляться через хранимые процедуры. Это предотвращает мошенничество не только инъекций SQl. Внутренний пользователь, который хочет совершить мошенничество, не должен иметь возможность напрямую изменять данные в таблицах базы данных. Это критический внутренний контроль в вашей системе. Вы действительно хотите, чтобы какой-то недовольный сотрудник перешел на бэкэнд вашей базы данных и начал ли его проверять? Или скрыть, что они одобрили расходы неавторизованному поставщику, когда у них нет полномочий по утверждению? Только два человека в вашей организации должны иметь возможность прямого доступа к данным в вашей финансовой базе данных, вашей dba и его резервной копии. Если у вас много dbas, только два из них должны иметь этот доступ.
Я упоминаю об этом, потому что, если ваши программисты использовали float в системе учета, вероятно, они полностью не знакомы с идеей внутреннего контроля и не рассматривали их в своих усилиях по программированию.
Ответ 14
Даже лучше, чем использование десятичных знаков, используется только простые старые целые числа (или, может быть, какой-то большой). Таким образом, вы всегда имеете максимальную точность, но точность может быть указана. Например, число 100
может означать 1.00
, которое отформатировано следующим образом:
int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);
Если вам нравится иметь больше точности, вы можете изменить значение 100 на большее значение, например: 10 ^ n, где n - число десятичных знаков.
Ответ 15
Вы всегда можете написать что-то вроде типа Money для .Net.
Взгляните на эту статью: Тип денег для CLR - автор сделал отличную работу, на мой взгляд.
Ответ 16
Я использовал тип денег SQL для хранения денежных значений. Недавно мне пришлось работать с несколькими онлайн-платежными системами и заметили, что некоторые из них используют целые числа для хранения денежных значений. В моих текущих и новых проектах я начал использовать целые числа, и я довольно доволен этим решением.
Ответ 17
Из 100 фракций n/100, где n - натуральное число, такое, что 0 <= n и n < 100, только четыре могут быть представлены как числа с плавающей запятой. Взгляните на вывод этой программы на C:
#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;
}
Ответ 18
Рассматривали ли вы использование типа денежных данных для хранения сумм в долларах?
Относительно Con, что десятичное число занимает еще один байт, я бы сказал, что это не волнует. В 1 миллионе строк вы будете использовать только 1 МБ, и в наши дни их очень дешево.
Ответ 19
Что бы вы ни делали, вам нужно быть осторожным с ошибками округления. Рассчитайте с большей степенью точности, чем вы показываете.
Ответ 20
Вероятно, вы захотите использовать некоторую форму представления фиксированной точки для значений валюты. Вы также захотите изучить округление Banker (также известное как "round half even".) Это позволяет избежать предубеждений, которые существуют в обычном методе "round half up".
Ответ 21
Ваши бухгалтеры захотят контролировать, как вы обходились. Использование float означает, что вы будете постоянно округлять, как правило, с помощью оператора типа FORMAT(), который не подходит вам (используйте вместо него пол/потолок).
У вас есть типы данных валюты (деньги, smallmoney), которые следует использовать вместо float или real. Хранение десятичного числа (12,2) устранит ваши округления, но также устранит их на промежуточных этапах - это действительно не то, что вы хотите вообще в финансовом приложении.
Ответ 22
Всегда используйте Decimal. Поплавок даст вам неточные значения из-за проблем округления.
Ответ 23
Числа с плавающей запятой могут представлять только числа, которые являются суммой отрицательных кратных базы - для двоичной с плавающей запятой, конечно, двух.
Существует только четыре десятичных дроби, представляемых точно в двоичной с плавающей запятой: 0, 0,25, 0,5 и 0,75. Все остальное является приближением, таким же образом, что 0.3333... является приближением для 1/3 в десятичной арифметике.
Плавающая точка является хорошим выбором для вычислений, где важна масштаб результата. Это плохой выбор, когда вы пытаетесь быть точным с некоторым количеством десятичных знаков.
Ответ 24
Это отличная статья, описывающая когда использовать float и decimal. Float хранит приблизительное значение, а десятичное значение сохраняет точное значение.
Таким образом, точные значения, такие как деньги, должны использовать десятичные числа, а приблизительные значения, такие как научные измерения, должны использовать float.
Вот интересный пример, который показывает, что и 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
При умножении не целого числа и делении на тот же номер, десятичные знаки теряют точность, а float - нет.
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