在大多数编程语言中,浮点数的表示方式与科学计数法非常相似:它包含一个指数和一个尾数(也称为有效数字)。一个非常简单的数,比如9.2
,实际上可以表示为以下分数:
5179139571476070 * 2 -49
其中指数为-49
,尾数为5179139571476070
。无法用这种方式表示某些十进制数的原因是指数和尾数都必须为整数。换句话说,所有浮点数都必须是一个整数乘以2的整数次幂。
9.2
可能只是92/10
,但如果将n
限制为整数值,10就不能表示为2n。
查看数据
首先,有几个函数可以“查看”组成32位和64位float
的组件。如果您只关心输出结果,请忽略这些(示例使用Python):
def float_to_bin_parts(number, bits=64):
if bits == 32:
int_pack = 'I'
float_pack = 'f'
exponent_bits = 8
mantissa_bits = 23
exponent_bias = 127
elif bits == 64:
int_pack = 'Q'
float_pack = 'd'
exponent_bits = 11
mantissa_bits = 52
exponent_bias = 1023
else:
raise ValueError, 'bits argument must be 32 or 64'
bin_iter = iter(bin(struct.unpack(int_pack, struct.pack(float_pack, number))[0])[2:].rjust(bits, '0'))
return [''.join(islice(bin_iter, x)) for x in (1, exponent_bits, mantissa_bits)]
那个函数背后有很多复杂性,解释起来会很离题,但如果你感兴趣的话,对我们来说重要的资源是struct模块。
Python的float
是64位双精度数字。在其他语言中,如C、C++、Java和C#,双精度有一个单独的类型double
,通常实现为64位。
当我们使用示例9.2
调用该函数时,我们得到以下结果:
>>> float_to_bin_parts(9.2)
['0', '10000000010', '0010011001100110011001100110011001100110011001100110']
解读数据
你会看到我将返回值分成了三个组成部分。这些组成部分包括:
符号
符号以单个位存储在第一个组成部分中。很容易解释:0
表示浮点数是正数;1
表示浮点数是负数。因为9.2
是正数,所以我们的符号值是0
。
指数
指数以11位的形式存储在中间组件中。在我们的例子中,是
0b10000000010
。换算成十进制,表示的值是
1026
。这个组件的一个特殊之处是,你必须减去一个等于
2(位数) - 1 - 1的数字才能得到真正的指数;在我们的例子中,意味着要减去
0b1111111111
(十进制数
1023
),得到真正的指数
0b00000000011
(十进制数3)。
尾数
尾数以52位的形式存储在第三个组件中。然而,这个组件也有一个特殊之处。为了理解这个特殊之处,考虑科学计数法中的一个数字,如下所示:
6.0221413x1023
尾数将是6.0221413。请记住,科学计数法中的尾数始终以一个非零数字开头。对于二进制来说也是如此,只不过二进制只有两个数字:0和1。因此,二进制尾数总是以1开头!当存储浮点数时,二进制尾数前面的1被省略以节省空间;我们必须将其放回到第三个元素的开头,以获得真正的尾数。
1.0010011001100110011001100110011001100110011001100110
这不仅仅是简单的加法,因为我们第三个组件中存储的位实际上代表了尾数的小数部分,在
小数点的右侧。
在处理十进制数时,我们通过乘以或除以10的幂来“移动小数点”。在二进制中,我们可以通过乘以或除以2的幂来做同样的事情。由于我们的第三个元素有52位,我们将它除以
252来将其向右移动52位:
0.0010011001100110011001100110011001100110011001100110
在十进制表示法中,这相当于将675539944105574除以4503599627370496得到0.1499999999999999。(这是一个比率的例子,在十进制中可以准确表示,但在二进制中只能近似表示;有关更多详细信息,请参见:
675539944105574 / 4503599627370496。)
现在我们已经将第三个组件转换为分数,加上1就得到了真正的尾数。
总结一下各个组成部分:
- 符号(第一个组件):正数为0,负数为1
- 指数(中间组件):减去2的(位数-1)次方-1,得到真正的指数
- 尾数(最后一个组件):除以2的(位数)次方,并加上1,得到真正的尾数
计算数字
将这三部分放在一起,我们得到以下二进制数:
1.0010011001100110011001100110011001100110011001100110 x 1011
然后我们可以将其从二进制转换为十进制:
1.1499999999999999 x 23(不精确!)
通过乘法运算,我们可以得到最终表示我们开始时的数字(9.2
),该数字被存储为浮点值。
9.1999999999999993
以分数形式表示
9.2
现在我们已经构建了这个数字,可以将其重构为一个简单的分数:
1.0010011001100110011001100110011001100110011001100110 x 10^11
将尾数移动到整数部分:
10010011001100110011001100110011001100110011001100110 x 10^(11-110100)
转换为十进制:
5179139571476070 x 2^(3-52)
减去指数:
5179139571476070 x 2^-49
将负指数转换为除法:
5179139571476070 / 2^49
乘以指数:
5179139571476070 / 562949953421312
等于:
9.1999999999999993
9.5
>>> float_to_bin_parts(9.5)
['0', '10000000010', '0011000000000000000000000000000000000000000000000000']
已经可以看到尾数只有4位数字,后面跟着一大串的零。但是让我们按部就班地进行下去。
组装二进制科学计数法:
1.0011 x 10^11
移动小数点:
10011 x 10^(11-100)
减去指数:
10011 x 10^-1
二进制转十进制:
19 x 2^-1
负指数转为除法:
19 / 2^1
乘以指数:
19 / 2
等于:
9.5
进一步阅读