关于C#:money不适合用double吗

Is a double really unsuitable for money?

我总是用C表示,双精度型变量不适合货币。所有奇怪的事情都可能发生。但我似乎无法创建一个例子来演示其中的一些问题。有人能举个例子吗?

(编辑:这篇文章最初被标记为C;一些回复提到了decimal的具体细节,这意味着System.Decimal)。

(编辑2:我特别要求一些C代码,所以我不认为这只是语言不可知论)


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

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

(这里是乔恩的页面示例-推荐阅读;-p)


四舍五入会有效地产生奇数错误。此外,与精确值进行比较非常困难——通常需要应用某种epsilon来检查实际值是否"接近"某个特定值。

下面是一个具体的例子:

1
2
3
4
5
6
7
8
9
10
11
using System;

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


是的,这不合适。

如果我记得正确的话,double大约有17个有效数字,所以通常舍入误差会远远超过小数点。大多数金融软件使用小数点后4位小数,剩下13位小数可供使用,因此单次操作所能使用的最大数字仍然远远高于美国国债。但舍入误差会随着时间的推移而增加。如果你的软件长时间运行,你最终会损失美分。某些操作会使情况更糟。例如,将大量添加到少量将导致严重的精度损失。

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

编辑根据这个网站http://msdn.microsoft.com/en-us/library/678hzk9.aspx doubles实际上有15到16个有效数字,而不是17个。

@由于精度较高,所以jon skeet decimal比double更适合28或29位有效小数。这意味着累积舍入误差变得显著的可能性更小。像boojum提到的固定点数据类型(即表示美分或百分之一的整数,如我所见)实际上更适合。


由于decimal使用的比例因子是10的倍数,因此0.1这样的数字可以精确表示。从本质上讲,decimal类型将其表示为1/10^1,而EDOCX1[1]将其表示为104857/2^20(实际上,它更像是真正的大数字/2^1023)。

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


我的理解是,大多数金融系统用整数来表示货币——也就是说,用美分来计算所有的东西。

ieee双精度实际上可以精确地表示-2^53到+2^53范围内的所有整数。(Hacker's Delight,第262页)如果您只使用加法、减法和乘法,并将所有内容都保持在这个范围内,那么您应该不会看到精度损失。不过,我对部门或更复杂的行动非常谨慎。


当你不知道自己在做什么的时候,就用双份。

"双倍"可以代表一万亿美元,误差为1/90美分。所以你会得到非常精确的结果。想知道把一个人放在火星上然后让他活着要花多少钱吗?双份正好。

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

根据你住的地方,用64位整数来表示美分、便士、Kopek或者其他任何你所在国家最小的单位,通常都可以。例如,表示美分的64位有符号整数可以表示高达92223万亿美元的值。32位整数通常不适用。


没有一个双精度数总是有舍入误差,如果你在.NET上,使用"decimal"…


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

请访问http://www.idinnews.com/moneyreep.html

固定点长度也是如此。两者都消耗8个字节,当然比小数项消耗的16个字节要好。

某件事是否有效(即产生预期和正确的结果)不是投票或个人偏好的问题。一种技术要么有效,要么无效。