【问题标题】:Why `sum([0.1] * 12) == 1.2` is True meanwhile `math.fsum([0.1] * 12) == 1.2` is False? [duplicate]为什么 `sum([0.1] * 12) == 1.2` 为真,而 `math.fsum([0.1] * 12) == 1.2` 为假? [复制]
【发布时间】:2017-12-12 06:58:27
【问题描述】:

在研究python内置的float函数时,我阅读了the floating point doc。并有所了解。

  • float 的实际值与其演示值不同,如0.1 的实际值为'0.1000000000000000055511151231257827021181583404541015625'
  • python 中的任何浮点数都有一个使用 IEEE-754 的固定值
  • math.fsum 为我们提供了与输入的精确数学总和最接近的精确可表示值

但是在做了一堆实验之后,我还是遇到了一些未解的疑惑。

怀疑1

在我在第一段提到的教程文档中,它给了我们一个例子:

>>> sum([0.1] * 10) == 1.0
False
>>> math.fsum([0.1] * 10) == 1.0
True

根据文档的说明,我的印象是math.fsum 在进行浮点求和时会给我们一个更准确的结果。

但我在range(20) 中发现了一个特殊情况,其中sum([0.1] * 12) == 1.2 评估为True,同时math.fsum([0.1] * 12) == 1.2 评估为False。这让我很困惑。

为什么会这样?
以及sum在做浮点求和时的机制是什么?

怀疑2

我发现对于一些浮点计算,加法运算与其等效的乘法运算具有相同的效果。比如0.1+0.1+0.1+0.1+0.1等于0.1*5。但在某些情况下,并不等价,例如将0.1 加起来 12 次不等于 0.1*12。这让我真的很困惑。根据浮点数是由 IEEE-754 标准计算的固定值。根据数学原理,这种加法应该等于它的等值乘法。唯一的解释是python在这里没有完全应用数学原理,发生了一些棘手的事情。

但是这个棘手的东西的机制和细节是什么?

In [64]: z = 0
In [64]: z = 0

In [65]: 0.1*12 == 1.2
Out[65]: False

In [66]: for i in range(12):
    ...:     z += 0.1
    ...:

In [67]: z == 1.2
Out[67]: True


In [71]: 0.1*5 == 0.5
Out[71]: True

In [72]: z = 0

In [73]: for i in range(5):
    ...:     z += 0.1
    ...:

In [74]: z == 0.5
Out[74]: True

【问题讨论】:

  • 我认为这可能是浮点数的原因,参考如下:stackoverflow.com/questions/2100490/…,如果你想避免这样的不准确,你可以尝试十进制数据类型。
  • 因为您已经发现浮点数是不准确的,所以永远不要指望== 会起作用,即使数学告诉您它应该起作用,即使有时它确实起作用(“偶然”)跨度>
  • @Julien,是的,困扰我的是浮点数上的加法不等于其等效乘法。这真的让我很困惑。你能给我一些提示,为什么会发生这种情况。是不是因为每次做加法时,中间的浮点结果都被四舍五入了?比如加0.1加12次,舍入12次,而0.1*12只舍入一次?
  • @Zen 没有“没有”理由为什么乘法应该给出与其重复加法“等效”相同的结果,因为它们遵循 2 个不同的路径/算法。虽然 x*y“是”x+x+x...y 次对于整数值,但这种解释对于非整数浮点数没有意义,因此这不是计算值的方式。
  • 我可能不应该这样做,但请查看 for i in range(100): for j in (0.1, math.pi, math.e): assert i*j==math.fsum(i*[j]) 之类的内容:-]

标签: python python-3.x floating-point precision floating-accuracy


【解决方案1】:

当 .1 转换为 64 位二进制 IEEE-754 浮点时,结果正好是 0.1000000000000000055511151231257827021181583404541015625。 12次单独相加,相加时会出现各种舍入误差,最终的和正好是1.1999999999999999555910790149937383830547332763671875。

巧合的是,当 1.2 转换为浮点时,结果也正好是 1.1999999999999999555910790149937383830547332763671875。这是一个巧合,因为添加 0.1 时的一些舍入误差向上舍入,一些向下舍入,最终结果为 1.19999999999999995555910790149937383830547332763671875。

但是,如果将 .1 转换为浮点数,然后使用精确数学相加 12 次,则结果正好是 1.20000000000000006661338147750939242541790008544921875。 Python 的math.fsum 可能会在内部产生这个值,但它不适合 64 位二进制浮点,所以它被四舍五入为 1.20000000000000017763568394002504646778106689453125。

可以看到的,更精确的值1.20000000000000017763568394002504646778106689453125不同于直接转化1.2至浮点,1.1999999999999999555910790149937383830547332763671875的结果,所以该比较报告它们不相等。 P>

this answer 中,我逐步添加了几个 .1 以详细检查舍入误差。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    • 2012-01-26
    • 2018-06-30
    • 2023-04-09
    • 1970-01-01
    • 2011-10-16
    相关资源
    最近更新 更多