为什么在 python 中 `0.4/2` 等于 `0.2` 而 `0.6/3` 等于 `0.19999999999999998`?

Why `0.4/2` equals to `0.2` meanwhile `0.6/3` equals to `0.19999999999999998` in python?

我知道这些是浮点除法。但是为什么这两个公式表现不同?

我又做了一些调查,结果更让我困惑:

>>>0.9/3
0.3

>>>1.2/3
0.39999999999999997

>>>1.5/3
0.5

这里判断结果是否打印一位或多位小数的逻辑是什么?

PS: 我用python3.4做了上面的实验

查看 floating point numbers in python 上的文档。

最具体地说:

Interestingly, there are many different decimal numbers that share the same nearest approximate binary fraction. For example, the numbers 0.1 and 0.10000000000000001 and 0.1000000000000000055511151231257827021181583404541015625 are all approximated by 3602879701896397 / 2 ** 55. Since all of these decimal values share the same approximation, any one of them could be displayed while still preserving the invariant eval(repr(x)) == x.

Historically, the Python prompt and built-in repr() function would choose the one with 17 significant digits, 0.10000000000000001. Starting with Python 3.1, Python (on most systems) is now able to choose the shortest of these and simply display 0.1.

因为浮点结果的精确值略有不同。

>>> '%.56f' % 0.4
'0.40000000000000002220446049250313080847263336181640625000'
>>> '%.56f' % (0.4/2)
'0.20000000000000001110223024625156540423631668090820312500'
>>> '%.56f' % 0.6
'0.59999999999999997779553950749686919152736663818359375000'
>>> '%.56f' % (0.6/3)
'0.19999999999999998334665463062265189364552497863769531250'
>>> '%.56f' % 0.2
'0.20000000000000001110223024625156540423631668090820312500'
>>> (0.2 - 0.6/3) == 2.0**-55
True

如您所见,打印为“0.2”的结果确实稍微接近0.2。我在末尾添加了一位,以向您展示这两个数字之间的差值的确切值。 (如果你很好奇,上面的表示是准确的值 - 添加任意数量的数字只会增加更多的零)。

根据 IEEE 754(几乎所有编程语言),浮点数实现为 binary64

这个标准给"significand / fraction" 52位(大约16位十进制数字的精度),11位给指数,1位给符号(正负):

特别是,像 0.4 这样的数字不能表示为

(1 + f) * 2**(exponent)

对于以 2 为底的某些分数和可以用 11 位(-1022 到 1023)表示的指数。

例如以十六进制查看 0.4

>>> (0.4).hex()
'0x1.999999999999ap-2'

我们看到我们的数字集中的最佳近似值是

+ 2**(-2) * (1 + 0x999999999999a/ float(2**52))

试图用基数 2 表示这个,我们有

2**(-2) * (1 + 0.6)

但以 2 为基数编写的 0.6 = 9/15 = 1001_2/1111_2 有一个由四个二进制数字组成的重复字符串

0.1001100011000110001...

因此永远无法使用有限数量的二进制数字来表示。


更深入一点

所以我们可以"unpack"0.4

>>> import struct
>>> # 'd' for double, '>' for Big-endian (left-to-right bits)
>>> float_bytes = struct.pack('>d', 0.4)

as 8 bytes (1 byte is 8 bits)

>>> float_bytes
'?\xd9\x99\x99\x99\x99\x99\x9a'

或 16 个十六进制数字(1 个十六进制数字为 4 位,因为 2**4 == 16

>>> ''.join(['%2x' % (ord(digit),) for digit in float_bytes])
'3fd999999999999a'

或作为所有 64 位的荣耀

>>> float_bits = ''.join(['%08d' % (int(bin(ord(digit))[2:]),)
...                       for digit in float_bytes])
>>> float_bits
'0011111111011001100110011001100110011001100110011001100110011010'

从那里开始,第一位是符号位:

>>> sign = (-1)**int(float_bits[0], 2)
>>> sign
1

接下来的 11 位是指数(但按惯例移动了 1023 binary64):

>>> exponent = int(float_bits[1:1 + 11], 2) - 1023
>>> exponent
-2

最后52位是小数部分

>>> fraction = int(float_bits[1 + 11:1 + 11 + 52], 2)
>>> fraction
2702159776422298
>>> hex(fraction)
'0x999999999999a'

综合起来

>>> sign * 2**(exponent) * (1 + fraction / float(2**52))
0.4