在大多数编程语言中,浮点数的表示方式非常类似于scientific notation:带有指数和尾数(也称为有效位)。一个非常简单的数字,比如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: # single precision
int_pack = 'I'
float_pack = 'f'
exponent_bits = 8
mantissa_bits = 23
exponent_bias = 127
elif bits == 64: # double precision. all python floats are this
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语言,双精度有一个单独的类型^ {< CD8> },通常是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被省略;我们必须把它放回第三个元素的前面,才能得到true尾数:1.0010011001100110011001100110011001100110011001100110
这不仅仅是简单的加法,因为存储在第三个组件中的位实际上表示尾数的小数部分,在radix point的右侧。
在处理十进制数时,我们用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 (inexact!)
并相乘以显示作为浮点值存储后以(9.2)开头的数字的最终表示:9.1999999999999993
以分数表示
9.2款
既然我们已经建立了这个数字,就可以把它重建成一个简单的分数:1.0010011001100110011001100110011001100110011001100110 x 1011
将尾数转换为整数:10010011001100110011001100110011001100110011001100110 x 1011-110100
转换为十进制:5179139571476070 x 23-52
减去指数:5179139571476070 x 2-49
将负指数转换为除法:5179139571476070 / 249
乘法指数:5179139571476070 / 562949953421312
等于:9.1999999999999993
9.5条>>> float_to_bin_parts(9.5)
['0', '10000000010', '0011000000000000000000000000000000000000000000000000']
你已经可以看到尾数只有4位数,后面跟着一大堆零。但让我们来看看步伐。
汇编二进制科学符号:1.0011 x 1011
移动小数点:10011 x 1011-100
减去指数:10011 x 10-1
二进制到十进制:19 x 2-1
负指数除法:19 / 21
乘法指数:19 / 2
等于:9.5
进一步阅读