IEEE 754浮点算术舍入错误在c#和javascript

IEEE 754 floating point arithmetic rounding error in c# and javascript

本文关键字:错误 javascript 舍入 IEEE      更新时间:2023-09-26

我刚刚读了一本关于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(又名decimal),这是一个十进制浮点类型,当你处理自然以十进制表示的数量时-通常是货币值。

有关更多信息,请参阅我关于。net二进制浮点数和十进制浮点数的文章。

这个错误是不是一个舍入误差,它只是一些值不能被IEEE 754标准精确地表示。请参阅Jon Skeet关于。net中二进制浮点数的文章。

对于处理像您的示例(基数为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.2999999999999999999 = d (3fd33333 33333333)

下一个大于0.2999999999999999999的可表示数是:

  • 0.30000000000000004 = d (3fd33333 33333334)

最接近的表示形式
  • 0.10000000000000001 + 0.20000000000000001 = 0.30000000000000004

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

就使用十进制代替二进制表示而言,这也不起作用。以三分之一为例:

  • 1/3 = 0.33333333333333333333333333333…

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

既然你已经知道了这个问题,那么解决方法就是在计算浮点数时记住它。

你的例子不完全是你会在一个真正的程序中使用的东西。但是如果真的需要的话,有一些方法可以评估这些东西,一个例子(在c#中)可以是…

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

这将是正确的,可能还有许多其他方法。关键是,如果你遇到这种情况,记住潜在的问题。正是这种经验造就了一个更好的开发人员/程序员