1. 浮点数的定义
先说说浮点数的表示方法。任意二进制数N可以写成
N
=
(
−
1
)
s
×
2
e
×
M
N = (-1)^s\times2^e\times M
N=(−1)s×2e×M 的形式,其中 s是符号位,e是指数,M 称为浮点数的尾数,是一个纯小数。参照 IEEE R32.24 标准,看看 float32 的格式:
下面我们来看一个实际例子, 8.25 8.25 8.25 用十进制表示是 82.5 × 1 0 − 1 82.5 \times 10^{-1} 82.5×10−1, 那么用二进制表示是多少呢?
8.25 → 1000.01 × 2 0 → 1.00001 × 2 3 (1) \tag1 8.25 \to 1000.01\times 2^0 \to 1.00001 \times 2^3 8.25→1000.01×20→1.00001×23(1)
那么好啊,既然所有的二进制都可以表示成1.xxx * 2^xxx,那么小数点前面的肯定都是1了所以在存储到内存的时候默认都是1.xxx就好了,也没必要在内存中存储1这个bit了所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里。
因此,32 位浮点数的实际表达形式变成了如下形式:
N
=
(
−
1
)
s
×
2
e
×
(
1
+
M
)
(2)
\tag2 N=(-1)^s \times 2^e \times (1 + M)
N=(−1)s×2e×(1+M)(2)
对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127 ~ 128了,所以指数部分的存储采用移位存储,存储的数据为原数据+127,下面就看看8.25在内存中真正的存储方式。
这样的话我们得到 8.25 的 float32 表示方法:
符号位 = 0
阶码 = 127 + 3 = 130
尾数 = 00001000....
2. 浮点数零
按照上面的定义,当
s
=
0
,
e
=
0
,
M
=
0
s=0,e=0,M=0
s=0,e=0,M=0 时,实际上表示的数值应该是
N
=
(
−
1
)
0
×
2
−
127
×
(
1
+
0
)
=
2
−
127
N=(-1)^0 \times 2^{-127} \times (1 + 0) = 2^{-127}
N=(−1)0×2−127×(1+0)=2−127
于是,我们发现,按照前面的定义,数字 0 是无法表示的。所以 IEEE R32.24 标准强行规定,
s
=
0
,
e
=
0
,
M
=
0
s=0,e=0,M=0
s=0,e=0,M=0 时,
N
=
0
N=0
N=0。
3. 浮点数在零附近的分布
我们只看正数吧!从数字 0 开始,从小到大列举最小的五个浮点数。
0
(
−
1
)
0
×
2
−
127
×
(
1
+
2
−
23
)
=
2
−
127
+
2
−
150
(
−
1
)
0
×
2
−
127
×
(
1
+
2
−
22
)
=
2
−
127
+
2
×
2
−
150
(
−
1
)
0
×
2
−
127
×
(
1
+
(
2
−
23
+
2
−
22
)
)
=
2
−
127
+
3
×
2
−
150
(
−
1
)
0
×
2
−
127
×
(
1
+
2
−
21
)
=
2
−
127
+
4
×
2
−
150
0\\ (-1)^0 \times 2^{-127}\times (1+2^{-23}) = 2^{-127}+2^{-150}\\ (-1)^0 \times 2^{-127}\times (1+2^{-22}) = 2^{-127}+2\times 2^{-150}\\ (-1)^0 \times 2^{-127}\times (1+(2^{-23} + 2^{-22})) = 2^{-127}+3\times2^{-150}\\ (-1)^0 \times 2^{-127}\times (1+2^{-21}) = 2^{-127}+4\times2^{-150}\\
0(−1)0×2−127×(1+2−23)=2−127+2−150(−1)0×2−127×(1+2−22)=2−127+2×2−150(−1)0×2−127×(1+(2−23+2−22))=2−127+3×2−150(−1)0×2−127×(1+2−21)=2−127+4×2−150
即
0
2
−
127
+
1
×
2
−
150
2
−
127
+
2
×
2
−
150
2
−
127
+
3
×
2
−
150
2
−
127
+
4
×
2
−
150
2
−
127
+
5
×
2
−
150
0\\ 2^{-127}+1\times2^{-150}\\ 2^{-127}+2\times2^{-150}\\ 2^{-127}+3\times2^{-150}\\ 2^{-127}+4\times2^{-150}\\ 2^{-127}+5\times2^{-150}\\
02−127+1×2−1502−127+2×2−1502−127+3×2−1502−127+4×2−1502−127+5×2−150
请注意,0 与其后面的最小数据增量是 2 − 127 2^{-127} 2−127,而后续的其他数据的增量是 2 − 150 2^{-150} 2−150。也就是说,数字在渐渐减少到 0 的过程中突然降到了 0。 为了减少 0 与最小数字和最小数字与次小数字之间步 长的突然下跌,subnormal 规定:当指数位全 0 的时候,指数表示为 -126 而不是 -127( 和指数为最低 位为 1 一致)。 然而公式改成 ( − 1 ) s × M × 2 e (-1)^s \times M \times 2^e (−1)s×M×2e, M 不再 +1, 这样最小的数字就变成 2 − 23 × 2 − 126 2^{- 23} \times 2^{- 126} 2−23×2−126, 次小的数字变成 2 − 22 × 2 − 126 2^{- 22} \times 2^{-126} 2−22×2−126, 每两个相邻 subnormal 数字 之差都是 2 − 23 × 2 − 126 2^{-23} \times 2^{-126} 2−23×2−126, 避免 了 突然 降到 0。