浮点数的表示

IEEE 754中的浮点数表示如下:

实际存储在内存中的是

  • 符号位
  • 指数
  • 分数值

在内存中的浮点数大概长这样1

memo-representation

IEEE 754规定了四种表示浮点数值的方式:

浮点数类型 符号位长度 指数位长度 分数位长度 总长度
单精确度 1 8 23 32
双精确度 1 11 52 64
延伸单精确度 1 - - 43+
延伸双精确度 1 - - 79+

本文为了节约篇幅,下面的讲解皆以单精度数为例,将其拓展到其他精度,只需扩展对应的位的长度即可。

符号位

代表浮点数本身的符号,老规矩,0正1负。

指数(exponent)

指数是一种特别的有符号数,它在计算机中的储存方式是这样的:

对于单精度浮点数,上述式子中的“指数位长度”可以用8代入:

这样,如果实际的指数数值是2,储存值就是$129_{10}$或${1000\ 0001}_2$。

如果是-3,储存值就是$124_{10}$或$0111\ 1100_2$。

特别的,储存值为${1111\ 1111}_2$或者说$255_{10}$时代表特殊情况,此时:

  • 分数值为0,符号位为0 —— 代表$+\infty$
  • 分数值为0,符号位为1 —— 代表$-\infty$
  • 分数值非0 —— 代表这个浮点数“不是数字”,多用在计算出现错误的情况下

另外,在储存值为$0$时,这是一个“非规格浮点数”,其实际值并非$127$而仍然是$126$,将在下一个话题中讲述。

为何这样储存,是因为这样在浮点数做比较的时候较为方便,直接按照字典序比较即可3

分数值/尾数(fraction)

分数值的储存要分类讨论:

  • 规格浮点数

    在指数储存值非 $0$ 也非 ${1111\ 1111}_2$ 时,这个浮点数是一个规格化浮点数。

    此时分数值表示的是一个$[1,2)$之间的数。

    储存方式如下:

    也就是说只保存小数点后的部分,1是隐含的,比如说表示$1.1_2$:

    再比如表示$1.11_2$。

    以此类推。

  • 非规格浮点数

    在指数储存值为 $0$ 时,这个浮点数是一个非规格化浮点数。

    此时分数值表示的是一个$[0,1)$之间的数。

    储存方式:

    如:

    实际上非规格浮点数做的事情就是填补距离 $0$ 最近的规格浮点数2

    与 $0$ 之间的空间。

    需要非规格浮点数的原因是为防止“突然式下溢出”。

突然式下溢出 & 渐进式下溢出

如果不采用非规格浮点数,那么能表示的最接近$0$的数和次接近$0$的数之间的差值为$(10)_2^{-23}(分数值差异)\times (10)_2^{-127}(指数)= (10)_2^{-150}$4

然而最接近$0$的数和$0$之间的差异仅有$(10)_2^{-127}$,如果你脑子里有数轴的话,你可以想象出这样一番场景:数轴从外往里浮点数能表示的数越来越密集(想一想,为什么),到了$0$点附近……

却突然空了。

感觉就很不舒服。

而且,如果你将明明不等的最接近$0$的数和次接近$0$的数相减……

答案却是0。

这就糟透了。

然后Intel的一群天才就强烈推荐IEEE采用渐进式下溢出

在使用了渐进式下溢出的情况下,最接近$0$的数就变成了非规格的:

它和次接近$0$的数与和$0$的差值都是其本身,使用这种方法从0~最小规格化浮点数之间的空间被非规格化浮点数均匀地填充了。

附表

32位浮点数情况:

类别 符号位 实际指数 有偏移指数 指数 尾数 十进制数值
0 -127 0 0000 0000 000 0000 0000 0000 0000 0000 0.0
负零 1 -127 0 0000 0000 000 0000 0000 0000 0000 0000 −0.0
1 0 0 127 0111 1111 000 0000 0000 0000 0000 0000 1.0
-1 1 0 127 0111 1111 000 0000 0000 0000 0000 0000 −1.0
最小的非规约数 * -126 0 0000 0000 000 0000 0000 0000 0000 0001 $±2^{−23} \times 2^{−126} = ±2^{−149} \approx ±1.4 \times 10^{-45}$
中间大小的非规约数 * -126 0 0000 0000 100 0000 0000 0000 0000 0000 $±2^{−1} \times 2 ^ {−126} = ±2^{−127} \approx ±5.88\times10^{-39}$
最大的非规约数 * -126 0 0000 0000 111 1111 1111 1111 1111 1111 $±(1−2^{−23}) \times 2^{−126} \approx ±1.18\times 10^{-38}$
最小的规约数 * -126 1 0000 0001 000 0000 0000 0000 0000 0000 $±2^{−126} \approx ±1.18×10^{-38}$
最大的规约数 * 127 254 1111 1110 111 1111 1111 1111 1111 1111 $±(2−2^{−23}) \times 2^{127} \approx ±3.4\times10^{38}$
正无穷 0 128 255 1111 1111 000 0000 0000 0000 0000 0000 +∞
负无穷 1 128 255 1111 1111 000 0000 0000 0000 0000 0000 −∞
NaN * 128 255 1111 1111 non zero NaN
1. 在小端序计算机(X86等)中,最右边的是最低字节的最低位,以float为例,从右往左数,[0-22]位是分数位,[23-30]是指数位,[31]是符号位。
2. 为讲解方便,在讨论“最接近0的浮点数”时一律取正数作为研究对象。
3. 实际上将符号位也反转过来,比较会更加容易,可以整个数不加修改地作字典序比较,人类感觉上也更加自然(0总感觉比1小),不过在电路上一个非门也费不了什么事
4. 维基和百度百科中此处写的是$2^{-126}$和$2^{-149}$,显然他们没有考虑到如果去掉非规格浮点数的设定后,可以有更多规格浮点数