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

时间:2015-03-09 04:28:14

标签: python floating-point floating-accuracy ieee-754

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

我做了更多调查,结果使我更加困惑:

>>>0.9/3
0.3

>>>1.2/3
0.39999999999999997

>>>1.5/3
0.5

这里有什么逻辑可以决定结果是打印一个小数位还是更多?

PS:我使用python3.4进行上述实验。

3 个答案:

答案 0 :(得分:6)

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

>>> '%.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。我在最后添加了这个位,以显示这两个数字之间差异的确切值。 (如果你很好奇,上面的表示是准确的值 - 添加任意数量的数字只会增加更多的零)。

答案 1 :(得分:5)

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

最具体:

  

有趣的是,有许多不同的十进制数共享相同的最接近的近似二进制分数。例如,数字0.1和0.10000000000000001和0.1000000000000000055511151231257827021181583404541015625都近似为3602879701896397/2 ** 55.由于所有这些十进制值共享相同的近似值,因此可以显示其中任何一个,同时仍保留不变eval(repr(x) )== x。

     

历史上,Python提示符和内置的repr()函数将选择具有17位有效数字的那一个,即0.10000000000000001。从Python 3.1开始,Python(在大多数系统上)现在能够选择最短的这些并且只显示0.1。

答案 2 :(得分:2)

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

该标准为"有效数/分数"提供了52位。 (精度约为16位小数),指数为11位,符号为1位(正负):

IEEE 754 bits

特别是,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...

所以永远不能使用有限数量的二进制数字来表示。


更深入

所以我们可以"解包" 0.4

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

为8个字节(1个字节为8位)

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