根据IEEE 754,浮点数被实现为binary64(几乎在所有编程语言中)。
此标准为“有效数/小数”提供 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)
但0.6 = 9/15 = 1001_2/1111_2 以 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