2008-11-25 5 views
65

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

(редактировать; этот пост был первоначально помечен C#; некоторые ответы относятся к конкретным деталям decimal, что поэтому означает System.Decimal).

(править 2: Я конкретно спрашиваю для некоторого C# кода, так что я не думаю, что это язык только агностик)

+0

[Почему бы не использовать Double или Float для представления валюты?] (Http://stackoverflow.com/q/3730019/995714) –

ответ

110

Очень, очень неподходящим. Используйте десятичное число.

double x = 3.65, y = 0.05, z = 3.7; 
Console.WriteLine((x + y) == z); // false 

(пример со страницы Джона here - рекомендуется прочитать ;-p)

+45

Обманите это, если бы я знал, что у меня есть пример на моей собственной странице, я бы 't придумал другой;) –

+7

Но эй, 2 примера лучше, чем 1 ... –

34

вы получите нечетные ошибки эффективно вызванные округлением. Кроме того, сравнение с точными значениями чрезвычайно сложно - вам обычно нужно применить какой-то epsilon, чтобы проверить, что фактическое значение «близко» к определенному.

Вот конкретный пример:

using System; 

class Test 
{ 
    static void Main() 
    { 
     double x = 0.1; 
     double y = x + x + x; 
     Console.WriteLine(y == 0.3); // Prints False 
    } 
} 
+0

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

+1

@vikingben: Абсолютно - в принципе, это испорченный способ делать вещи, и вам нужно разобраться, как лучше всего интерпретировать данные. –

+0

Спасибо, похоже, у меня есть работа. – vikingben

7

Да это неподходящее.

Если я правильно помню, что double имеет около 17 значащих чисел, то обычно ошибки округления будут значительно отставать от десятичной точки. Большинство финансовых программ использует 4 десятичных знака за десятичной точкой, что позволяет работать с 13 десятичными знаками, поэтому максимальное число, с которым вы можете работать для отдельных операций, по-прежнему намного выше, чем государственный долг США. Но ошибки округления будут складываться со временем. Если ваше программное обеспечение работает в течение длительного времени, вы в конечном итоге начнете проигрывать центы. Определенные операции сделают это хуже. Например, добавление больших количеств в небольшие количества приведет к значительной потере точности.

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

редактировать
Согласно этому сайту http://msdn.microsoft.com/en-us/library/678hzkk9.aspx У парного разряда на самом деле есть от 15 до 16 значащих цифр вместо 17.

@ Jon Skeet decimal более подходит, чем двойной, из-за его высокой точности, 28 или 29 значащих десятичных знаков. Это означает, что становится меньше шансов на накопление ошибок округления. Фиксированные точечные типы данных (т. Е. Целые числа, которые представляют центы или 100-й процент, как я видел, используются), как упоминания Boojum, на самом деле лучше подходят.

+0

Обратите внимание, что System.Decimal, предлагаемый тип для использования в .NET, по-прежнему является типом с плавающей точкой, но это плавающая десятичная точка, а не плавающая двоичная точка. Я подозреваю, что это более важно, чем фиксированная точность в большинстве случаев. –

+1

Вот в чем проблема. Валюта в настоящее время обычно десятичная. Однако до того, как американские фондовые рынки разделились, бинарные фракции использовались (я начал видеть 256-е и даже 1024-е годы в одной точке), и поэтому удвоения были бы более подходящими, чем десятичные значения для цен на акции! Предварительная децимализация фунтов стерлингов была бы настоящей болью, хотя в 960 фунтов к фунту; это не десятичная, ни двоичная, но она, безусловно, обеспечивает большое количество простых факторов для простых фракций. –

+1

Даже более важно, чем просто десятичная плавающая запятая, 'decimal' выражение' x + 1! = X' всегда истинно. Кроме того, он сохраняет точность, поэтому вы можете определить разницу между «1» и «1.0». – Gabe

5

С decimal использует коэффициент масштабирования кратных 10, цифры, как 0,1, могут быть представлены точно. По сути, десятичный тип представляет это как 1/10^1, тогда как double будет представлять это как 104857/2^20 (в действительности это было бы больше похоже на действительно большое число/2^1023).

A decimal может точно представлять любое базовое значение 10 до 28/29 значащих цифр (например, 0,1). A double не может.

+3

Десятичная цифра не имеет 96 значащих цифр. Он имеет 96 значащих * бит *. Decimal имеет около 28 значащих цифр. –

+0

На каком языке вы говорите о десятичном типе? Или все языки, поддерживающие этот тип, поддерживают его точно так же? Может потребоваться указать. –

+0

@Adam - этот пост первоначально имел тег C#, поэтому мы говорим о System.Decimal в частности. –

4

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

IEEE двойная точность фактически может представляет все целые числа точно в диапазоне от -2^53 до +2^53. (Hacker's Delight, стр. 262). Если вы используете только сложение, вычитание и умножение и сохраняете все до целых чисел в этом диапазоне, то вы не увидите потери точности. Однако я бы очень опасался разделения или более сложных операций. не

+0

Если вы собираетесь использовать целые числа, почему бы не использовать целочисленный тип для начала? –

+2

Хех - int64_t может представлять все целые числа точно в диапазоне от -2^63 до +2^63-1. Если вы используете только сложение, вычитание и умножение и сохраняете все до целых чисел в этом диапазоне, вы не увидите потери точности. Однако я бы очень опасался разделения. –

+0

Некоторые устаревшие системы, которые (увы?) Все еще используются, поддерживают 'double', но не поддерживают какой-либо 64-разрядный целочисленный тип. Я бы предположил, что выполнение вычислений как 'double', масштабируется, что любое семантически требуемое округление всегда будет для целых единиц, является наиболее эффективным подходом. – supercat

0

Нет двойной всегда будет иметь ошибки округления, используйте «десятичный», если вы на .Net ...

+6

Осторожно. * Любое * представление с плавающей запятой будет иметь ошибки округления, включая десятичные числа. Это просто, что десятичное число будет округлено, что будет интуитивно понятным для людей (и, как правило, подходит для денег), а двоичная с плавающей запятой не будет. Но для нефинансовых чисел хруст, double часто намного, намного лучше десятичного, даже в C#. –

2

Использование двойной, когда вы не знаете, что вы делаете непригоден.

"double" может представлять собой сумму в триллион долларов с ошибкой 1/90-го процента. Таким образом, вы получите очень точные результаты. Хотите рассчитать, сколько стоит положить мужчину на Марс и вернуть его обратно? двойной будет делать только отлично.

Но с деньгами часто бывают очень конкретные правила, говорящие, что определенный расчет должен дать определенный результат, а другой нет. Если вы подсчитаете сумму, которая очень очень близка к $ 98.135, тогда часто будет правило, определяющее, должен ли результат быть $ 98.14 или $ 98.13, а вы должны следовать этому правилу и получить требуемый результат.

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

-4

Фактически с плавающей точкой double идеально подходит для представления суммы денег, пока вы выбираете подходящее устройство.

См http://www.idinews.com/moneyRep.html

Так фиксированной точкой долго. Либо потребляет 8 байтов, что, безусловно, предпочтительнее 16, потребляемых десятичным числом.

Независимо от того, работает ли что-либо (т. Е. Дает ожидаемый и правильный результат), не зависит ни от голосования, ни от индивидуальных предпочтений. Техника работает или нет.

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^