1. 浮点数的二进制转化
以
9.
1
10
9.1_{10}
9.110 为例。
9
10
=
100
1
2
9_{10} = 1001_2
910=10012,再把小数位转换为二进制。以
0.100
1
2
0.1001_2
0.10012 为例:
0.100
1
2
=
1
×
2
−
1
+
0
×
2
−
2
+
0
×
2
−
3
+
1
×
2
−
4
=
0.562
5
10
0.1001_2 = 1 \times 2^{-1} + 0 \times 2^{-2} + 0 \times 2^{-3} + 1 \times 2^{-4} = 0.5625_{10}
0.10012=1×2−1+0×2−2+0×2−3+1×2−4=0.562510
小数部分转换成二进制是乘以 2, 然后看是否超过 1,如果超过 1,我们就记下 1,并把结果减去 1, 进一步循环操作。
0.1 其实就变成一个无限循环的二进制小数,0.000110011。“0011” 会无限循环下去。
9.
1
10
=
1001.00011001
1
2
=
1.001000110011
…
×
2
3
2
9.1_{10} = 1001.000110011_2 = {1.001000110011\ldots \times 2^3}_2
9.110=1001.0001100112=1.001000110011…×232
对应符号位 s = 0,有效位 f = 001000110011……。因为 f 最长只有 23 位,所以 “0011” 循环到 23 位就截止了。于是:
f
=
00100011001100110011001
f = 00100011001100110011001
f=00100011001100110011001
最后一个 “0011” 循环中的最后一个 “1” 会被截掉。对应指数位 e = 3。因为指数位有正又有负,所以指数位在 127 之前代表负数,之后代表正数,那 3 其实对应的是加上 127 的偏移量 130,转化成二进制,就是 130,对应的就是指数位的二进制,表示出来就是 10000010。
把 “s + e + f” 拼在一起,浮点数 9.1 的二进制就表示为:
010000010 0010 0011001100110011 001
如果我们再把这个浮点数表示换算成十进制, 实际准确值是 9.09999942779541015625(这和 f 位丢失有关)。
2. 浮点数的加法和精度损失
浮点数的加法计算原理是:先对齐、再计算。
两个浮点数的指数位可能是不一样的,所以我们要把两个的指数位,变成一样的,然后只去计算有效位的加法就好了。
以 0.5 + 0.125 的浮点数运算为例。首先要把两个的指数位对齐,也就是把指数位都统一成两个其中较大的 -1。对应的有效位 1.00…也要对应右移两位,因为 f 前面有一个默认的 1,所以就会变成 0.01。然后我们计算两者相加的有效位 1.f,就变成了有效位 1.01,而指数位是 -1,这样就得到了我们想要的加法后的结果。
浮点数加法过程中,其中指数位较小的数,需要在有效位进行右移,在右移的过程中,最右侧的有效位就被丢弃掉了。这会导致对应的指数位较小的数,在加法发生之前,就丢失精度。两个相加数的指数位差的越大,位移的位数越大,可能丢失的精度也就越大(大数吃小数)。
32 位浮点数的有效位长度一共只有 23 位,如果两个数的指数位差出 23 位,较小的数右移 24 位之后,所有的有效位就都丢失了。这也就意味着,虽然浮点数可以表示上到 3.40×1038,下到 1.17×10−38 这样的数值范围。但是在实际计算的时候,只要两个数,差出 224,也就是差不多 1600 万倍,那这两个数相加之后,结果完全不会变化。
参考
极客时间《深入浅出计算机组成原理》:http://gk.link/a/11UMi