为什么`sum([0.1] * 12)== 1.2`是真的同时`math.fsum([0.1] * 12)== 1.2`是假的?

时间:2017-12-12 06:58:27

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

在研究python内置浮点函数时,我读了the floating point doc。并得到了一些了解。

  • Float的实际价值与其示范价值不同,例如0.1的实际值为'0.1000000000000000055511151231257827021181583404541015625'
  • python中的任何浮点数都使用IEEE-754
  • 具有固定值
  • math.fsum为输入的确切数学和提供最接近的可精确表示的值

但在做了一堆实验后,我仍然遇到一些未解决的疑问。

Doubt1

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

>>> 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 evals True,同时math.fsum([0.1] * 12) == 1.2 evals False。这让我感到困惑。

为什么会这样? 进行浮点求和时sum的机制是什么?

Doubt2

我找到了一些浮点计算,加上运算与它的等效乘法运算具有相同的效果。例如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

1 个答案:

答案 0 :(得分:1)

当.1转换为64位二进制IEEE-754浮点时,结果正好是0.1000000000000000055511151231257827021181583404541015625。当您单独添加12次时,在添加期间会发生各种舍入错误,最终总和恰好是1.1999999999999999555910790149937383830547332763671875。

巧合的是,当1.2转换为浮点时,结果也正好是1.1999999999999999555910790149937383830547332763671875。这是巧合,因为添加.1的一些舍入误差向上舍入,有些向下舍入,最终产生了1.1999999999999999555910790149937383830547332763671875。

但是,如果.1转换为浮点数然后使用精确数学加法12次,则结果正好是1.20000000000000006661338147750939242541790008544921875。 Python的math.fsum可能在内部生成此值,但它不适合64位二进制浮点数,因此它舍入为1.20000000000000017763568394002504646778106689453125。

正如您所看到的,更准确的值1.20000000000000017763568394002504646778106689453125与将1.2直接转换为浮点数的结果不同,因此比较报告它们是不相等的。

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