关于c#:存储舍入的双精度会导致不直观的结果

Storing rounded doubles results in un-intuitive results

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Why is floating point arithmetic in C# imprecise?

如果我循环遍历许多随机双精度数,并将它们"四舍五入"到两个小数位数,则每个单精度数似乎都是正确的(0.02、0.01、0.00等)。

然而,似乎有一个非常小的分数部分是随着圆。

1
2
3
4
5
6
7
8
double total = 0;

for (int i = 0; i < 10000; i++)
{
    total += Math.Round(random.NextDouble() * 0.02, 2);
}

Console.WriteLine(total);

样品输出:

100.60亿06

99.74亿

有人愿意解释为什么这是一种更直观的方式吗?


System.Double和System.Float是基2浮点类型。有许多有限的十进制值以2为底具有无限表示,多达1/3以10为底具有无限表示。因此,当您四舍五入到这样的值时,二进制表示是近似的。要避免此问题,请使用十进制类型,它是以10为基数的浮点类型。

StackOverflow上一定有100个问题的副本,但是我在电话里,这使得查找和链接这些问题变得不方便。

有关更多信息,请参阅wikipedia上的ieee double文章。

很多人会说双打"不准确",这是错误的。每一个双精度值代表一个精确的值,它可以以10为底精确表示(当然,除了NaN和Infinity)。这是因为2是10的主要因素之一。唯一的近似值是当你试图表示某些小数(或其他有理数,其分母至少有一个素数,而不是2)。

至少对我来说,理解这一点的最好方法是在纸上算出几个分数的二进制表示。例如,尝试0.5、0.625、3.25、5/16、1/3、0.2和0.3。


double不以10为基数存储数值-它们以2为基数存储数值,因此,在存储分数时,它们与预期的十进制值可能会有微小的差异。就其价值而言,这并不是基2所独有的。Base 10(实际上所有Base-N系统)都有相同的问题-例如1/3。在基数10中,您最终将其表示为类似于0.3333333(…)的值,但没有办法在基数10中完美地表示1/3。

在您的示例中,在表示数字的小数部分时可能会遇到一些小错误,并且由于将这些小错误相加,您可能会看到这些小错误在累积。用我上面的例子,如果你把.333333(…)四舍五入到小数点后2位,你会得到.33,但是相对于1/3的实际值,这有相当大的不准确度。在进行浮点运算时,积累这些错误是一个常见的错误。

正如@phoog所写,关于这一点有很多解释。这里有一个:为什么C中的浮点运算不精确?