【问题标题】:IEEE 754 floating point arithmetic rounding error in c# and javascriptc# 和 javascript 中的 IEEE 754 浮点算术舍入错误
【发布时间】:2012-01-20 03:35:09
【问题描述】:

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

例如,将 0.1 和 0.2 相加得到 0.30000000000000004 而不是 0.3。

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

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

所以我的问题是:

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

【问题讨论】:

  • 有一本书称这是“舍入错误”??
  • 这不是一个“错误”,因为它是设计使然,但它很麻烦。我确信在某些情况下这将是有用的行为,但在实践中(至少在 JavaScript 中)我认为如果默认表示为“正确”(精确的以 10 为底)十进制表示会更有用。我真的从来不想要一个 binary 浮点数。
  • 这本书是 Nicholas C. Zakas 的“面向 Web 开发人员的 JavaScript”第 2 版。第 33 页描述了“错误”。

标签: c# javascript floating-point ieee-754


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    该错误不是舍入错误,只是某些值不能由 IEEE 754 标准精确表示。如需进一步阅读,请参阅 Jon Skeet 在binary floating point numbers in .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
    }
    

    【讨论】:

      【解决方案3】:

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

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

      有关详细信息,请参阅我在 .NET binary floating pointdecimal floating point 上的文章。

      【讨论】:

        【解决方案4】:

        既然您知道了这个问题,解决方法是在计算浮点数时牢记这一点。

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

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

        这是真的,可能还有很多其他方法。重点是,如果您遇到过这种情况,请记住潜在的问题。正是这种体验造就了更好的开发人员/程序员

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-30
          • 2013-07-24
          • 2020-06-09
          • 2012-01-06
          相关资源
          最近更新 更多