c#和javascript中的IEEE 754浮点运算舍入误差

我刚读了一本关于javascript的书。 作者提到了IEEE 754标准中的浮点算术舍入误差。

例如,添加0.1和0.2会产生0.30000000000000004而不是0.3。

所以(0.1 + 0.2) == 0.3返回false。

我也在c#中重现了这个错误。

所以这些是我的问题是:

这种错误多久发生一次? c#和javascript中的最佳实践解决方法是什么? 哪些其他语言有相同的错误?

这不是语言中的错误。 这不是IEEE 754中的错误。二进制浮点数的期望和使用是错误的。 一旦你理解了什么是二进制浮点数,它就非常有意义。

C#中的最佳实践是使用System.Decimal (aka decimal )这是一个十进制浮点类型,只要您处理以十进制自然表示的数量 – 通常是货币值。

有关更多信息,请参阅有关.NET 二进制浮点和十进制浮点的文章。

错误不是舍入错误,只是某些值无法由IEEE 754标准准确表示。 请参阅Jon Skeet关于.net中二进制浮点数的文章, 以供进一步阅读。

为了处理像你的例子(base-10)这样的数字,你应该在C#中使用decimal数据类型,因为它可以准确地表示这些数字,所以你得到了你期望的值。

一种典型的方法是定义一些epsilon值,并检查结果是否在targetvalue + – epsilon中:

 double const epsilon = 0.000001; // or whatever if(valueA >= valueB - epsilon && valueA <= valueB + epsilon) { // treat as valueA = valueB } 

双精度浮点中这三个数的最接近的表示是:

  • 0.1 – > 0.10000000000000001 = D(3FB99999 9999999A)
  • 0.2 – > 0.20000000000000001 = D(3FC99999 9999999A)
  • 0.3 – > 0.29999999999999999 = D(3FD33333 33333333)

下一个更大的可表示数字超过0.29999999999999999是:

  • 0.30000000000000004 = D(3FD33333 33333334)

最接近的代表

  • 0.10000000000000001 + 0.20000000000000001是0.30000000000000004

所以你要比较0.29999999999999999和0.30000000000000004。 这是否能让您更深入地了解正在发生的事情?

至于使用十进制而不是二进制表示,这也不起作用。 以三分之一为例:

  • 1/3 = 0.3333333333333333333333333333333 …

即使使用十进制数字也没有确切的表示。 任何计算都应该始终考虑表示错误。

那么现在您已经了解了这个问题,解决方法是在评估浮点数时牢记这一点。

您的示例并不完全是您在真实程序中使用的内容。 但是如果确实需要,有一些方法可以评估这些事情,一个例子(在C#中)可能是……

 if((0.1f + 0.2f).ToString("0.0") == "0.3") 

这将是真的,可能还有很多其他方法。 重点是,如果您有这种情况,那么请记住潜在的问题。 正是这种经验使得更好的开发人员/程序员