存储圆形双打会导致不直观的结果

时间:2012-02-29 05:30:38

标签: c# rounding

  

可能重复:
  Why is floating point arithmetic in C# imprecise?

如果我循环遍历多个随机双打,并将它们“舍入”到两个小数位,则每个单独的圆形似乎是正确的(0.02,0.01,0.00等)。

然而,似乎有一个非常小的部分与圆形一起保留。

double total = 0;

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

Console.WriteLine(total);

示例输出:

100.600000000006

99.7400000000059

任何人都在关心解释为什么会发生这种情况更为直观?

2 个答案:

答案 0 :(得分:4)

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

在stackoverflow上必须有100个这个问题的重复,但我在手机上,这使得找到它们并链接到它们不方便。

有关更多信息,请查看维基百科文章中的IEEE double。

很多人会说双打“不准确”,这是错误的。每个双精度值代表一个精确值,可以用基数10表示(当然,NaN和无穷大除外)。这是因为2是10的主要因素之一。唯一的近似是当你试图表示某些小数部分(或其分母至少有一个除2之外的一个素数因子的其他有理数)。

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

答案 1 :(得分:0)

双打不存储10个基数 - 它们在基数2中存储一个值,因此在存储小数时,它们可能与预期的小数值有很小的差异。对于它的价值,这不是基础2所特有的。基础10(实际上所有基础N系统)都有相同的问题 - 例如1/3。在基数10中,你最终将其表示为0.3333333(...),但是没有办法在基数10中完美地表示1/3。

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

正如@Phoog所写,在SO上有很多解释。这是一个:Why is floating point arithmetic in C# imprecise?