浮点数可以理解为计算机里的“二进制科学计数法”。 IEEE 754 规定了符号、指数、小数部分的存储方式,也规定了零、无穷大和 NaN 等特殊值。

例如,十进制的 123.45=1.2345×102123.45=1.2345\times10^2;二进制的 101.1012=1.011012×22101.101_2=1.01101_2\times2^2。通过调整指数,浮点数能在有限位数内兼顾很大和很小的数,但并非每个实数都能精确表示。

IEEE 754 的字段

常见的二进制格式如下:

格式 总位数 符号位 SS 指数位 EE 小数位 FF 偏置值 Bias 有效二进制位
单精度 float 32 1 8 23 127 24
双精度 double 64 1 11 52 1023 53

单精度的排列是 S | EEEEEEEE | FFFFFFFFFFFFFFFFFFFFFFF。S=0S=0 表示正,S=1S=1 表示负,因此符号因子为 (−1)S(-1)^S。

偏置指数与隐藏位

指数可能为负,但指数位按无符号数存储。对规格化数,存储的指数 EE 与真实指数的关系是:

Ereal=E−Bias.E_{\text{real}}=E-\text{Bias}.

例如单精度的真实指数为 22 时,存储 2+127=129=1000000122+127=129=10000001_2;真实指数为 −3-3 时,存储 −3+127=124-3+127=124。

规格化后的二进制有效数字写作 1.F21.F_2。最高位的 1 恒定,无需实际存储,这就是隐藏位;只存小数点后的 FF。因此单精度的 23 位小数可提供 24 位二进制有效精度,约 7 位十进制有效数字;双精度为 53 位,约 15~16 位十进制有效数字。

重点: 仅当指数位既不全为 0、也不全为 1 时,才使用隐藏的 1。此时数值为

(−1)S×(1.F)2×2E−Bias.\boxed{(-1)^S\times(1.F)_2\times2^{E-\text{Bias}}}.

例如 −6.5=−1.1012×22-6.5=-1.101_2\times2^2,其中符号位为 1,真实指数为 2。

编码实例:将 5.75 写成单精度浮点数

  1. 转成二进制:5.75=101.112=1.01112×225.75=101.11_2=1.0111_2\times2^2。
  2. 符号位 S=0S=0;指数存储值 E=2+127=129=100000012E=2+127=129=10000001_2。
  3. 隐去有效数字开头的 1.,小数部分 0111 补足 23 位,得到 01110000000000000000000。

拼接后得到:

0 | 10000001 | 01110000000000000000000

对应的 32 位十六进制表示是 0x40B80000。

特殊编码

单精度和双精度的指数、尾数组合对应如下:

单精度指数 单精度尾数 双精度指数 双精度尾数 表示的对象
0 0 0 0 0
0 非 0 0 非 0 正负非规格化数
1~254 任意数 1~2046 任意数 正负浮点数
255 0 2047 0 正负无穷
255 非 0 2047 非 0 NaN(非数)

正负符号由单独的符号位决定,因此表中的 0 也有 +0+0 和 −0-0 两种编码。中间一行的正负浮点数属于规格化数,使用隐藏的 1。

正负零、无穷大与 NaN

正零和负零只差符号位,通常比较为相等;在遵循 IEEE 754 的浮点环境中,1/(+0)=+∞1/(+0)=+\infty,而 1/(−0)=−∞1/(-0)=-\infty。指数全为 1 且小数全为 0 时表示无穷大,符号仍由 SS 决定。

NaN 表示没有正常实数结果的情况,例如实数浮点计算中的 −1\sqrt{-1}。它与自身比较也不相等:NaN == NaN 为假。判断 NaN 应使用语言提供的函数,如 C/C++ 的 isnan(x),而不是比较 x == NAN。

非规格化数与渐进下溢

指数全为 0 且小数非 0 时,隐藏位改为 0,指数按 1−Bias1-\text{Bias} 计算:

(−1)S×(0.F)2×21−Bias.\boxed{(-1)^S\times(0.F)_2\times2^{1-\text{Bias}}}.

单精度对应的指数为 −126-126。非规格化数填补了最小规格化数与 0 之间的空隙,使数值可以逐渐接近 0,这称为渐进下溢。

精度限制与比较

为什么 0.1 不能精确存储?

十进制的 0.10.1 转为二进制是无限循环小数:

0.110=0.0001100110011…2.0.1_{10}=0.0001100110011\ldots_2.

小数位有限,存储时必须舍入。因此 0.1 + 0.2 在常见双精度计算中可能得到 0.30000000000000004:参与计算的 0.1 和 0.2 本来就是近似值。

相邻浮点数的间距并不固定

指数越大,相邻可表示数的间距通常越大。例如单精度在 224=167772162^{24}=16777216 附近已无法表示下一个整数 1677721716777217。在常见的舍入方式下,16777216.0f + 1.0f 仍可能得到 16777216。

比较浮点结果

对计算得到的浮点数,0.1 + 0.2 == 0.3 可能为假。可根据问题允许的误差,检查 ∣a−b∣<ε|a-b|<\varepsilon;ε\varepsilon 需要结合数值规模选择,不能在所有场景固定使用同一个值。


看来我的智力确实是超进化了,
我记得大二上课我都没搞明白浮点数,
现在一下就明白了,哈哈。😄 甘奈