it-swarm-ru.tech

Использовать число с плавающей запятой или десятичное число для учета долларов США?

Мы переписываем нашу устаревшую систему учета в VB.NET и SQL Server. Мы привлекли новую команду программистов .NET/SQL для переписывания. Большая часть системы уже укомплектована суммами в долларах с использованием чисел с плавающей запятой. У старого системного языка, который я запрограммировал, не было Float, поэтому я, вероятно, использовал бы десятичное число.

Какова ваша рекомендация?

Следует ли использовать тип данных Float или Decimal для сумм в долларах?

Каковы некоторые плюсы и минусы для обоих?

Одна из оговорок, упомянутая в нашей ежедневной схватке, заключалась в том, что вы должны быть осторожны, когда вычисляете сумму, которая возвращает результат, превышающий две десятичные позиции. Похоже, вам придется округлить сумму до двух знаков после запятой.

Еще один минус - все показы, и напечатанные суммы должны иметь оператор формата, который показывает две десятичных позиции. Я заметил несколько раз, где это не было сделано, и суммы не выглядели правильными. (т.е. 10,2 или 10,2546)

Профи - это то, что Float занимает всего 8 байт на диске, тогда как десятичное число будет занимать 9 байт (десятичное 12,2). 

74
Gerhard Weiss

Следует ли использовать тип данных 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), чтобы мы могли записать "миллиардные пенни", которые были получены.

Не помогает против этой ошибки. Поскольку все люди автоматически предполагают, что компьютер суммирует правильно, практически никто не проверяет самостоятельно.

105
TSK
41
Nakilon

Сначала вы должны прочитать это Что каждый ученый должен знать об арифметике с плавающей точкой . Тогда вам действительно стоит подумать об использовании некоторого типа числа с фиксированной точкой/произвольной точности пакета (например, Java BigNum, десятичный модуль python), иначе вы попадете в мир боли. Затем выясните, достаточно ли использовать собственный десятичный тип SQL. 

Существуют плавающие/двойные (ред.), Чтобы выставить быстрый x87 fp, который сейчас в значительной степени устарел. Не используйте их, если вы заботитесь о точности вычислений и/или не полностью компенсируете их ограничения.

22
Rich Schuler

В качестве дополнительного предупреждения SQL Server и платформа .Net используют другой алгоритм округления по умолчанию. Убедитесь, что вы проверили параметр MidPointRounding в Math.Round (). .Net Framework по умолчанию использует алгоритм Банкерса, а SQL Server использует симметричное алгоритмическое округление. Проверьте статью в Википедии здесь

10
Darrel Miller

Спросите своих бухгалтеров! Они будут осуждать вас за использование поплавка. Как и кто-то опубликовал ранее, используйте float ТОЛЬКО, если вам не нужна точность. Хотя я всегда был бы против этого, когда дело доходит до денег.

В бухгалтерском программном обеспечении не допускается использование поплавка. Используйте десятичную с 4 десятичными точками.

7
Ricardo C

Плавающие точки имеют неожиданные иррациональные числа.

Например, вы не можете хранить 1/3 как десятичное число, это было бы 0.3333333333 ... (и так далее)

Плавания на самом деле хранятся в виде двоичного значения и степени 2 степени.

Таким образом, 1,5 сохраняется как 3 х 2 до -1 (или 3/2)

Используя эти показатели base-2, создайте несколько нечетных иррациональных чисел, например:

Конвертируйте 1.1 в число с плавающей точкой и затем снова конвертируйте его, ваш результат будет примерно таким: 1.0999999999989

Это связано с тем, что двоичное представление 1.1 на самом деле 154811237190861 x 2 ^ -47, с чем может справиться больше, чем double.

Подробнее об этой проблеме на моем блоге , но в основном для хранения лучше использовать десятичные дроби.

На сервере Microsoft SQL у вас есть тип данных money - это обычно лучше для финансового хранения. Это с точностью до 4 знаков после запятой.

Для вычислений у вас есть большая проблема - неточность - крошечная доля, но поместите ее в степенную функцию, и она быстро становится существенной. 

Однако десятичные дроби не очень хороши для любого вида математики - например, нет встроенной поддержки десятичных степеней.

6
Keith

Немного предыстории здесь ....

Ни одна система счисления не может точно обработать все действительные числа. У всех есть свои ограничения, и это включает как стандартную IEEE с плавающей запятой, так и десятичную со знаком. IEEE с плавающей точкой является более точным для каждого используемого бита, но здесь это не имеет значения.

Финансовые показатели основаны на многовековой практике бумажных и ручек с соответствующими соглашениями. Они достаточно точны, но, что более важно, они воспроизводимы. Два бухгалтера, работающие с разными номерами и тарифами, должны прийти к одному и тому же номеру. Любая комната для несоответствия есть место для мошенничества.

Следовательно, для финансовых расчетов правильный ответ - это то, что дает тот же ответ, что и CPA, который хорош в арифметике. Это десятичная арифметика, а не с плавающей точкой IEEE.

5
David Thornley

Используйте тип сервера SQL decimal.

Не используйте money или float.

деньги используют 4 знака после запятой, это быстрее, чем использование десятичного числа , НО страдает от некоторых очевидных и некоторых не столь очевидных проблем с округлением ( см. эту проблему подключения

5
Mitch Wheat

Я бы порекомендовал использовать 64-битные целые числа, которые хранят все это в центах.

5
Joshua

Плавания не являются точными представлениями, возможны проблемы точности, например, при добавлении очень больших и очень маленьких значений. Вот почему десятичные типы рекомендуются для валюты, хотя точность может быть достаточно редкой.

Для пояснения, десятичный тип 12,2 будет точно хранить эти 14 цифр, тогда как float не будет, так как он использует двоичное представление внутри. Например, 0,01 не может быть представлено точно числом с плавающей запятой - самое близкое представление фактически 0,0099999998

4
Niall

За банковскую систему, в которой я помогал развиваться, я отвечал за часть начисления процентов. Каждый день мой код вычислял, сколько процентов было начислено (заработано) на балансе в тот день.

Для этого расчета требовалась предельная точность и достоверность (мы использовали FLOAT от Oracle), чтобы мы могли фиксировать накопленные «миллиардные доли пенни».

Когда дело дошло до «капитализации» процентов (т.е. выплаты процентов обратно на ваш счет), сумма была округлена до копейки. Тип данных для остатков на счетах был два десятичных знака. (На самом деле это было сложнее, так как это была мультивалютная система, которая могла работать во многих десятичных разрядах - но мы всегда округляли до «копейки» этой валюты). Да - там, где «доли» потерь и прибылей, но когда цифры компьютеров были актуализированы (деньги выплачены или выплачены), это всегда были РЕАЛЬНЫЕ денежные ценности.

Это удовлетворило бухгалтеров, аудиторов и тестировщиков.

Итак, уточняйте у ваших клиентов. Они расскажут вам свои банковские/бухгалтерские правила и практики.

4
Guy

Единственная причина использовать Float для денег, если вы не заботитесь о точных ответах. 

4
David Singer

Еще одна вещь, которую вы должны знать в учетных системах, это то, что никто не должен иметь прямой доступ к таблицам. Это означает, что весь доступ к системе учета должен осуществляться через хранимые процедуры. Это предотвращает мошенничество, а не только инъекции SQl. Внутренний пользователь, который хочет совершить мошенничество, не должен иметь возможности напрямую изменять данные в таблицах базы данных. Это критический внутренний контроль в вашей системе. Вы действительно хотите, чтобы какой-то недовольный сотрудник пошел в бэкэнд вашей базы данных и начал писать им чеки? Или скрыть, что они утвердили расходы неавторизованному поставщику, если у них нет полномочий на утверждение? Только два человека во всей вашей организации должны иметь прямой доступ к данным в вашей финансовой базе данных, вашей базе данных и его резервной копии. Если у вас много dbas, только два из них должны иметь этот доступ.

Я упоминаю об этом, потому что, если ваши программисты использовали float в системе учета, скорее всего, они совершенно не знакомы с идеей внутреннего контроля и не учитывали их в своих усилиях по программированию.

3
HLGEM

Даже лучше, чем использование десятичных чисел, это использование простых старых целых чисел (или, возможно, какого-то типа bigint). Таким образом, вы всегда имеете максимально возможную точность, но точность можно указать. Например, число 100 может означать 1.00, которое имеет следующий формат:

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

Если вы хотите повысить точность, вы можете изменить значение 100 на большее, например: 10 ^ n, где n - количество десятичных знаков.

3
Peter Stuifzand

Из 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;
}
2
Lars Bohl

Вы всегда можете написать что-то типа Money для .Net.

Взгляните на эту статью: Тип денег для CLR - Автор сделал отличную работу, на мой взгляд.

2
Tomer Pintel

Я использовал SQL тип денег для хранения денежных значений. Недавно мне приходилось работать с рядом систем онлайн-платежей, и я заметил, что некоторые из них используют целые числа для хранения денежных значений. В моих текущих и новых проектах я начал использовать целые числа, и я довольно доволен этим решением.

2
George

Возможно, вы захотите использовать некоторую форму представления с фиксированной точкой для значений валюты. Вы также захотите исследовать округление Банкира (также известное как «округление до половины». Это позволяет избежать смещения, существующего в обычном методе «округления до половины».

1
user6931

Рассматривали ли вы использовать тип данных money для хранения долларовых сумм?

Что касается Con, что десятичное число занимает еще один байт, я бы сказал, что это не волнует. В 1 миллионе строк вы будете использовать только еще 1 МБ, а хранилище очень дешево в наши дни.

1
Espo

Что бы вы ни делали, вы должны быть осторожны с ошибками округления. Рассчитать, используя большую степень точности, чем вы отображаете в.

1
1800 INFORMATION

Ваши бухгалтеры захотят контролировать, как вы округляетесь. Использование float означает, что вы будете постоянно округлять, как правило, с помощью оператора типа FORMAT (), а это не совсем то, что вам нужно (вместо этого используйте floor/floor).

У вас есть типы данных валюты (money, smallmoney), которые следует использовать вместо float или real. Хранение десятичной дроби (12,2) устранит ваши округления, но также устранит их на промежуточных этапах - что на самом деле совсем не то, что вам нужно в финансовом приложении.

0
David T. Macknet

Это отличная статья, описывающая когда использовать float и decimal . 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

При умножении нецелого числа и делении на это же число десятичные дроби теряют точность, а плавающие - нет.

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
0
BrokeMyLegBiking

Всегда используйте десятичную. Float выдаст вам неточные значения из-за проблем с округлением.

0
Roel Vlemmings

Числа с плавающей запятой могут только представлять числа, которые являются суммой отрицательных кратных основания - для двоичной с плавающей запятой, конечно, это два.

Есть только четыре десятичных дроби, представляемых точно в двоичной с плавающей запятой: 0, 0,25, 0,5 и 0,75. Все остальное является приближением, так же, как 0,3333 ... является приближением для 1/3 в десятичной арифметике.

С плавающей точкой хороший выбор для вычислений, где важен масштаб результата. Это плохой выбор, когда вы пытаетесь быть точными с некоторым количеством знаков после запятой.

0
Mike Dimmick