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

Hen*_*enk 3 javascript c# floating-point ieee-754

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

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

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

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

所以这些是我的问题是:

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

Jon*_*eet 9

这不是语言中的错误.它在IEEE 754中不是错误.这是二进制浮点数的期望和使用中的错误.一旦你明白什么二进制浮点数字真的,它非常有意义.

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

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


Geo*_*ett 5

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

为了处理像您的示例(以 10 为基数)这样的数字,您应该使用decimalC# 中的数据类型,因为它可以准确地表示这些数字,这样您就可以获得您期望的值。

典型的方法是定义一些epsilon值,并检查结果是否在 targetvalue +- epsilon 范围内:

double const epsilon = 0.000001; // or whatever

if(valueA >= valueB - epsilon && valueA <= valueB + epsilon)
{
    // treat as valueA = valueB
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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

  • 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...

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