IEEE 754 浮点数表示

系列 - CSAPP 笔记
注意
本文最后更新于 2022-03-29,文中内容可能已过时。

浮点数Floating Point是表示实数的一种方法. 它的思想类似于科学计数法, 即将数字写成 $x = (-1)^S M \cdot 2^E$ 的形式. 那么需要记录 符号Sign $S$, 尾数Mantissa $M$ 和指数或阶(码)Exponent $E$.

IEEE 754 是 IEEE 指定的一种浮点数标准, 规定了单精度single-precision双精度double-precision浮点数的尾数和阶码的编码.

IEEE 754 标准按照这样的规则编码浮点数: 1 位符号数, k 位阶码, w 位尾数. 下面是单精度和双精度的阶码以及尾数的长度规定

精度 位数 阶码(k) 尾数(w)
单精度 32 8 23
双精度 64 11 52
floating.png
浮点数规定 (图源于CSAPP)

IEEE 754 对浮点数的规定可以大致分为三种情况, 标志就是阶码 $E$ 是全 0 还是全 1, 或者是其他. 全 0 和全 1 用来表示非一般情况的数.

规格(化)Normalized 数是最常见的情况. 标志是阶码非全 0 也非全 1. 此时, 阶码以 8 位无符号整数编码, 但是有一个偏移Bias. 中文也把加上偏移的无符号整数编码称作 移码. 具体来说, 真值 $E = e - Bias$ ($e$ 是阶码). 其中 $Bias = 2^{k-1} - 1$. 对单精度浮点数来说, $Bias = 127$. 这样 $E$ 的取值就是 $[-126, 127]$. 至于为什么取 $Bias = 2^{k-1} - 1$, 主要是实现数字的平滑过度. 具体看非规格化数和特殊数.

规格化数之所以叫规格化, 是因为他把尾数(的真值) $m$ 规定成 $1.\mathrm{XXX}$ 的形式, 这和科学计数法是一样的. 如果尾数不是这个形式, 则需要调整, 即左移或者右移尾数, 然后对指数进行加减操作. 由于规格数小数点前面一位永远是 $1$, 所以可以省略. 这样可以多存一位数字. $1 \le M < 2$

注意
中文教材有时把符号位放到尾数上, 然后说这是定点小数原码编码

非规格(化)Denormalized 数的阶码为全 0. 此时认为 $E = 1 - Bias$. 对单精度来说, 就是 $E = -126$. 然后尾数 没有前导 $1.$, 也就是说, $0 \le M < 1$.

注意到规格化数并没有 0 这个数. 因为尾数始终大于等于 1. 而非规格化数没有前导 $1.$, 当尾数为全 0 时, 就表示 0 了. 并且符号数不同时, 可以表示 $+0.0$ 和 $-0.0$. 这两个数在某些情况下看作不同的数.

在单精度规格数中, 最小的正数是 $1 \cdot 2^{-126}$, 再小就无法表示了. 而非规格数补充了 $x \cdot 2^{-126}, 0 \le x < 1$. 这样就把中间 $0$ 到 $1 \cdot 2^{-126}$ 用 $2^{23}$ 个数均匀填充, 可以表示这些接近 0 的数了. 这个性质叫逐渐下溢gradual underflow.

同时, 我们还能够看到, 最大的非规格化数是 $0\ 00000000\ 11111111111111111111111$, 如果我们继续 (这个数可以看成是 0 逐渐加 $2^{-23}$ 上来的) 对尾数最低位加 1, 可以得到 $0\ 00000001\ 00000000000000000000000$, 这是规格数最小值. 注意阶码为 $00000001$, 指数真值为 $E = 1 - Bias = -126$. 所以 $Bias$ 的取法和非规格化数的定义是很非常巧妙的.

特殊Special 数是一些特殊的值, 比如 $\infty$ 和 $NaN$. 标志为阶码全 1. 当尾数为全 0 时, 表示 $\infty$; 非全 0 时, 表示 $NaN$Not a Number.

单精度浮点数正最大规格数是 $0\ 11111110\ 11111111111111111111111$, 当它再加上最小分辨率后, 变成了 $0\ 11111111\ 00000000000000000000000$, 也就是 $+\infty$. 从这里也可以看出 $Bias$ 的取法和 $\infty$ 的定义是非常巧妙的.

/floating-point-representation/categories.png
单精度浮点数类型 (图源于CSAPP)

需要指出的是, 这种表示方法和十进制的小数一样, 只能表示特定的点, 不能表示所有的实数, 不能表示的数在运算 (赋值当然也是一种运算) 的时候, 会进行舍入Round.