我总是在 c# 中告诉 double 类型的变量不适合钱。所有奇怪的事情都可能发生。但我似乎无法创建一个示例来演示其中的一些问题。谁能提供这样的例子?
(编辑;这篇文章最初被标记为 C#;一些回复提到了 的具体细节decimal
,因此意味着System.Decimal
)。
(编辑 2:我特别要求一些 c# 代码,所以我认为这不仅仅是语言不可知的)
我总是在 c# 中告诉 double 类型的变量不适合钱。所有奇怪的事情都可能发生。但我似乎无法创建一个示例来演示其中的一些问题。谁能提供这样的例子?
(编辑;这篇文章最初被标记为 C#;一些回复提到了 的具体细节decimal
,因此意味着System.Decimal
)。
(编辑 2:我特别要求一些 c# 代码,所以我认为这不仅仅是语言不可知的)
非常非常不合适。使用小数。
double x = 3.65, y = 0.05, z = 3.7;
Console.WriteLine((x + y) == z); // false
(来自 Jon 的页面示例- 推荐阅读;-p )
四舍五入会导致您有效地得到奇怪的错误。此外,与精确值的比较非常棘手——您通常需要应用某种 epsilon 来检查实际值是否“接近”特定值。
这是一个具体的例子:
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/678hzkk9.aspx双打实际上有 15 到 16 位有效数字而不是 17。
@Jon Skeet decimal 比 double 更合适,因为它的精度更高,有 28 位或 29 位有效小数。这意味着累积舍入误差变得显着的可能性较小。像 Boojum 提到的定点数据类型(即代表美分或 100 美分的整数)实际上更适合。
由于decimal
使用 10 的倍数的比例因子,因此可以精确表示 0.1 之类的数字。本质上,十进制类型将其表示为 1 / 10 ^ 1,而 adouble
将其表示为 104857 / 2 ^ 20(实际上它更像是真的大数/ 2 ^ 1023)。
Adecimal
可以精确地表示任何具有最多 28/29 位有效数字(如 0.1)的基数为 10 的值。一个double
不能。
我的理解是,大多数金融系统使用整数表示货币——即,以美分计算所有内容。
IEEE 双精度实际上可以精确地表示 -2^53 到 +2^53 范围内的所有整数。(Hacker's Delight,第 262 页)如果您只使用加法、减法和乘法,并将所有内容保持在此范围内的整数,那么您应该不会看到精度损失。但是,我会非常警惕分裂或更复杂的操作。
当您不知道自己在做什么时使用 double 是不合适的。
“double”可以代表一万亿美元的金额,误差为 1/90 美分。因此,您将获得高度精确的结果。想计算将一个人送上火星并让他活着回来需要多少钱?双倍会做得很好。
但是对于金钱,通常有非常具体的规则说,一定的计算必须给出一定的结果,而不是其他的。如果您计算的金额非常非常接近 98.135 美元,那么通常会有一条规则确定结果应该是 98.14 美元还是 98.13 美元,您必须遵循该规则并获得所需的结果。
根据您居住的地方,使用 64 位整数来表示美分、便士或戈比或您所在国家/地区的任何最小单位通常都可以正常工作。例如,代表美分的 64 位有符号整数可以代表高达 92,223 万亿美元的价值。32 位整数通常不适合。
没有双精度数总是会出现舍入错误,如果您在.Net 上,请使用“十进制”...
实际上,只要您选择合适的单位, 浮点双精度数就非常适合表示金额。
见http://www.idinews.com/moneyRep.html
定点long也是如此。要么消耗 8 个字节,当然比十进制项目 消耗的 16 个字节更可取。
某事是否有效(即产生预期和正确的结果)与投票或个人偏好无关。一项技术要么有效,要么无效。