在大多数编程语言中,浮点数的表示非常像科学记数法:具有指数和尾数(也称为有效位数)。一个非常简单的数字,9.2
实际上就是这个分数:
5179139571476070 * 2 -49
指数在哪里,-49
尾数在哪里5179139571476070
。无法以这种方式表示一些十进制数字的原因是,指数和尾数都必须是整数。换句话说,所有浮点数必须是整数乘以2的整数次幂。
9.2
可以很简单92/10
,但是如果n限制为整数,则10不能表示为2 n。
看到数据
首先,使用一些函数来查看组成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#)中,双精度具有单独的类型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.0221413x10 23
尾数将是6.0221413
。回想一下,科学计数法中的尾数始终以单个非零数字开头。二进制也是如此,只是二进制只有两位数字:0
和1
。因此二进制尾数总是以1
!当存储浮点数时,将1
省略二进制尾数的前面以节省空间。我们必须将其放回第三个元素的前面以获取真实的尾数:
1.0010011001100110011001100110011001100110011001100110110
这不仅仅涉及简单的加法,因为存储在我们的第三个分量中的位实际上代表了尾数的小数部分,即小数点右边。
在处理十进制数字时,我们通过乘以10的乘方或除以“移动小数点”。在二进制中,我们可以通过乘以2的乘方或除以进行相同的操作。由于我们的第三个元素有52位,因此我们除以通过2 52将其向右移动52个位置:
0.0010011001100110011001100110011001100110011001100110110
在十进制,这是同分675539944105574
的4503599627370496
获得0.1499999999999999
。(这是一个比率的示例,该比率可以精确地用二进制表示,但只能近似用十进制表示;有关更多详细信息,请参见:675539944105574/4503599627370496。)
现在,我们已经将第三个分量转换为分数,加法1
给出了真实的尾数。
重新盖上组件
- 符号(第一部分):
0
为正,1
为负
- 指数(中间部分):减去2 (#位) - 1 - 1获得真正的指数
- 尾数(最后一个分量):除以2 (位数),然后加上
1
即可得到真实的尾数
计算数字
将所有三个部分放在一起,我们得到这个二进制数字:
1.0010011001100110011001100110011001100110011001100110 x 10 11
然后我们可以将其从二进制转换为十进制:
1.1499999999999999 x 2 3(不准确!)
并相乘以显示以(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
进一步阅读