真的不适合钱吗?


70

我总是在c#中告诉您double类型的变量不适合钱。所有奇怪的事情都可能发生。但是我似乎无法创建一个示例来演示其中的一些问题。谁能提供这样的例子?

(编辑;此帖子最初被标记为C#;一些回复涉及的具体细节decimal,因此,这意味着System.Decimal)。

(编辑2:我是专门要求一些C#代码的,所以我不认为这仅与语言无关)


Answers:


115

非常非常不合适。使用十进制。

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

(例如,从Jon的页面在这里-推荐阅读;-p)


48
该死,如果我知道自己的页面上有一个示例,我不会想出一个不同的示例;)
Jon Skeet

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
    }
}

如果您使用的服务会返回无法控制的双币种值,那么在将它们转换为十进制时是否需要考虑一些陷阱?精度损失等...
vikingben '16

1
@vikingben:绝对-从根本上讲,这是一种破损的处理方式,您需要确定如何最好地解释数据。
乔恩·斯基特

7

是的,这不合适。

如果我没记错的话,double大约有17个有效数字,所以通常舍入错误将发生在小数点后。大多数财务软件在小数点后使用4位小数,这使13位小数可以使用,因此一次操作可以使用的最大数目仍然远远高于美国国债。但是舍入错误会随着时间的推移加总。如果您的软件运行了很长时间,您最终将开始损失美分。某些操作会使情况更糟。例如,将大量添加到少量将导致精度显着下降。

您需要定点数据类型来进行货币操作,大多数人不介意您在这里和那里失去一分钱,但会计师并不像大多数人那样。


根据此网站进行编辑http://msdn.microsoft.com/en-us/library/678hzkk9.aspx双打居然有15至16位显著,而不是17。

@Jon Skeet十进制比double更适合,因为它具有更高的精度,即28或29个有效小数。这意味着累积的舍入误差出现的可能性较小。像Boojum提到的定点数据类型(例如,代表美分或美分的整数)实际上更适合。


请注意,System.Decimal(建议在.NET中使用的类型)仍然是浮点类型-但这是浮点小数点而不是浮点二进制点。我怀疑,在大多数情况下,这比固定精度更为重要。
乔恩·斯基特

1
这正是问题所在。如今,货币通常为小数。但是,早在美国股票市场十进制化之前,就已经使用了二进制分数(我开始看到256位,甚至是1024位),因此对于股价而言,双精度比十进制更合适!十进制前的英镑虽然是960英镑的英镑,但却是一个真正的痛苦。它既不是十进制也不是二进制,但是它确实为简单的分数提供了大量的质因子。
杰弗里·汉汀

1
decimal该表达式x + 1 != x始终比是正确的,甚至比仅保留十进制浮点数更为重要。另外,它还保持精度,因此您可以分辨出1和之间的区别1.0
加布

@Gabe:只有当一个人缩放自己的值以使值1表示最小货币单位时,这些属性才有意义。一个Decimal值可能会失去小数点右边的精度,而不表示任何问题。
2013年

double仅具有15.9个有效十进制数字(仅考虑整数值)。小数点后的情况取决于值。
user207421 '18

5

由于decimal使用的比例因子为10的倍数,因此可以精确表示0.1之类的数字。本质上,十进制类型将其表示为1/10 ^ 1,而adouble则将其表示为104857/2 ^ 20(实际上,它更像是真正的大数字/ 2 ^ 1023)。

Adecimal可以精确地代表任何以10为基数的值,最多可包含28/29个有效数字(例如0.1)。一个double不能。


3
十进制没有96位有效数字。它具有96个有效。小数有大约28个有效数字。
乔恩·斯基特

您所用的十进制是哪种语言?还是所有支持这种类型的语言都以完全相同的方式支持它?可能要指定。
亚当·戴维斯

@Adam-该帖子最初具有C#标记,因此我们专门讨论System.Decimal。
马克·格雷韦尔

糟糕,乔恩!已更正。亚当,根据问题,我在说C#。是否有其他语言具有称为十进制的类型?
理查德·普尔

@Richard:好吧,所有基于.NET的语言都可以,因为System.Decimal不是唯一的C#类型,所以它是.NET类型。
敬畏

4

我的理解是,大多数金融系统都使用整数表示货币-即以美分来计数所有内容。

IEEE双精度实际上可以表示介于-2 ^ 53到+ 2 ^ 53之间的所有整数。(Hacker's Delight,第262页)如果仅使用加,减和乘,并将所有内容都保持在此范围内的整数,那么您应该不会损失任何精度。但是,我会非常谨慎地对待部门或更复杂的业务。


如果您只打算使用整数,为什么不使用整数类型开始呢?
乔恩·斯基特

2
嘿-int64_t可以表示介于-2 ^ 63至+ 2 ^ 63-1之间的所有整数。如果仅使用加法,减法和乘法,并且将所有内容都保持在此范围内的整数,那么您应该不会损失任何精度。但是,我会非常谨慎地对待分裂。
史蒂夫·杰索普

某些仍在使用的过时系统(alas?)支持double,但不支持任何64位整数类型。我建议将按double语义进行的舍入按比例进行缩放,以使语义上需要的舍入始终以整单位为单位,这将是最有效的方法。
supercat 2012年

3

当您不知道自己在做什么时使用double是不合适的。

“ double”可以代表一万亿美元的金额,其误差为1/90美分。这样您将获得高度精确的结果。想计算将一个人送上火星并使他复活的费用吗?加倍就可以了。

但是对于金钱,通常有非常具体的规则说,某种计算必须给出某种结果,而没有其他结果。如果您计算的金额非常非常接近$ 98.135,那么通常会有一个规则来确定结果应为$ 98.14还是$ 98.13,并且您必须遵循该规则并获得所需的结果。

根据您的住所,使用64位整数代表美分,便士或科比或您所在国家/地区中最小的单位通常都可以用。例如,代表美分的64位有符号整数可以表示高达92,223万亿美元的值。通常不适合使用32位整数。


0

没有双精度型将始终存在舍入错误,如果您使用的是.Net,请使用“十进制” ...


6
小心。 任何浮点表示形式都将具有舍入误差,包括十进制在内。只是十进制会以人类直观的方式四舍五入(并且通常适合金钱),而二进制浮点则不会。但是对于非金融数字运算,即使在C#中,双精度通常也比十进制好得多。
丹尼尔·普赖登2009年

-4

实际上,只要选择一个合适的单位,浮点精度就非常适合表示金额。

参见http://www.idinews.com/moneyRep.html

因此定点。要么消耗8个字节,肯定好于十进制消耗的16个字节项。

某事是否有效(即产生预期的正确结果)与投票或个人偏好无关。一项技术有效或无效。


链接您写的一篇文章,该文章与数十年来的惯常做法和专家选择不同,后者认为浮点数不适合进行金融交易表示,因此需要比单个页面多备份一些。
MuertoExcobito 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.