本地资料浮点数表示
SCHEDULE LOCAL7 个小节覆盖真题 20092025
关联考点IEEE浮点数表示12浮点数加减2做相关真题 · 14 道 →
做相关真题 · 14 道选中文字可高亮或加下划线
选中文字高亮 · 下划线

浮点数表示

复习提示(高优先级):IEEE 754 的字段含义、规格化数与非规格化数、边界值及浮点加减步骤是本页主线。

真题练习

IEEE 浮点数表示的方法非常重要,需要能够熟练计算 IEEE 浮点数的二进制和十进制之间的关联。与此同时,还要能够解释有限位宽带来的舍入误差。

实数的二进制表示

实数在计算机中的存储遵循 IEEE 浮点数标准,但在这里为了方便理解 IEEE 标准,这里首先阐明一般 实数 在计算机中是如何存储的。

在这里 实数 分为两个部分存储:整数部分小数部分,两个部分在逻辑上用 · 隔开。

比如对于以下 浮点数 的二进制表示:

dmdm1d1d0.d1d2dnd_m d_{m-1}\cdots d_1d_0.d_{-1}d_{-2}\cdots d_{-n}

其中每一位对应的数值如下表所示:

数位 dmd_m dm1d_{m-1} \cdots d1d_1 d0d_0 d1d_{-1} d2d_{-2} \cdots dnd_{-n}
位权 2m2^m 2m12^{m-1} \cdots 212^1 202^0 212^{-1} 222^{-2} \cdots 2n2^{-n}

上述二进制表示对应的数值为

b=i=nmdi2ib=\sum_{i=-n}^{m} d_i2^i

其中 di=0d_i=0di=1d_i=1,表示第 ii 位是 0 还是 1。

举例说明如何使用上述表示法计算 实数

  • (101.11)2=1×22+0×21+1×20+1×21+1×22=5.75(101.11)_2=1\times2^2+0\times2^1+1\times2^0+1\times2^{-1}+1\times2^{-2}=5.75
  • (1011.1)2=8+0+2+1+12=11.5(1011.1)_2=8+0+2+1+\frac{1}{2}=11.5

十进制字面值转二进制

举个实际的例子,将 1.2 转换为二进制表示。

整数部分 1 的二进制是 1。 小数部分 0.2 的二进制表示是一个无限循环小数。通过不断乘以 2,可以得到近似的二进制:

  • 0.2×2=0.40.2\times2=0.4 → 整数部分 0;
  • 0.4×2=0.80.4\times2=0.8 → 整数部分 0;
  • 0.8×2=1.60.8\times2=1.6 → 整数部分 1;
  • 0.6×2=1.20.6\times2=1.2 → 整数部分 1;
  • 0.2×2=0.40.2\times2=0.4 → 重复……

于是,1.2 在二进制中近似为 1.0011001100110011...

IEEE 754 浮点数表示

上述 浮点数 存储方式的缺点在于,如果要表示比较大的数,就需要比较多的二进制位数,比如对于 5×21005\times2^{100},直接写出完整二进制定点形式至少需要 103 个整数位。IEEE 表示就解决了这个问题,它采用类似科学计数法的表示。在 IEEE 表示中,浮点数 VV 被表示为:

V=(1)s×M×2EV=(-1)^s\times M\times2^E

其中 s,M,Es,M,E 的含义如下:

  • s 为 符号位
  • M 为 乘法因子
  • E 为 指数部分

IEEE 754 标准 是定义浮点数表示和算术的国际标准,它定义了多种不同精度的浮点数格式,但最常见的是 单精度 single precesion(32 位)和 双精度 double precesion(64 位),浮点数分为 s符号位)、exp阶码)、frac尾数)三个部分存储:

IEEE 754 浮点数由符号位、阶码与尾数字段组成(原资源 inline SVG 1)
  • 1 位的 s 与上述计算公式中的 符号位 s 相同,位于浮点数二进制表示的 最高位,标志了浮点数的正负:如果 s=1s=1VV 是负数;如果 s=0s=0VV 为正数。

  • k 位的 exp 字段为 (ek1e1e0)2(e_{k-1}\cdots e_1e_0)_2,用于计算 指数部分 EEexp 为 unsigned 值;对规格化数,它不能为全 0 或全 1,其中

    E=expBias,Bias=2k11E=exp-Bias,\qquad Bias=2^{k-1}-1
    • 对于 单精度 浮点数,k=8k=8Bias=2811=127Bias=2^{8-1}-1=1271exp2541\le exp\le254126E127-126\le E\le127
    • 对于 双精度 浮点数,k=11k=11Bias=21111=1023Bias=2^{11-1}-1=10231exp20461\le exp\le20461022E1023-1022\le E\le1023
  • n 位的 frac 字段表示 (0.f1f2fn)2(0.f_1f_2\cdots f_n)_2,用于计算 因子 MM,其中 M=1+fracM=1+fracfrac 表示的小数计算方式见前面的“实数的二进制表示”;规格化数最高位的 11 隐含而不存储。

    • 对于 单精度n=23n=231M22231\le M\le2-2^{-23}
    • 对于 双精度n=52n=521M22521\le M\le2-2^{-52}

总结 单精度双精度 浮点数 表示如下:

类型 符号位 ss 阶码 expexp 尾数 fracfrac 总位数 偏置值
单精度 1 8 23 32 127
双精度 1 11 52 64 1023

单精度 浮点数表示为:

(1)s×(1.frac)2×2exp127(-1)^s\times(1.frac)_2\times2^{exp-127}

双精度 浮点数表示为:

(1)s×(1.frac)2×2exp1023(-1)^s\times(1.frac)_2\times2^{exp-1023}

总结 单精度 和 双精度 浮点数中各个字段的范围如下图所示:

IEEE 754 单精度与双精度各字段的位宽和取值范围(原资源 inline SVG 2)

非规格化数、特殊值与异常值

注意 IEEE 浮点数 表示分为 Normalized Values(正常值)和 Denormalized Values(非正常值)以及特殊值,上节中提到的 IEEE 浮点数 计算方法只适用于 正常值正常值阶码(exp)不能为全 0 或全 1。

术语上,正常值也称 规格化数,非正常值也称 非规格化数;下文同时保留这两组称呼,含义相同。

下图是 浮点数 各种类型的图示(以 单精度 为例):

IEEE 754 单精度中规格化数、非规格化数、无穷大与 NaN 的字段组合(原资源 inline SVG 3)
  • 非正常值(Denormalized)的 阶码全为 0
  • 无穷大(Infinity)的 阶码全为 1,尾数位为 0
  • 非数字(NaN,Not a Number)的 阶码全为 1,尾数位不为 0

需要注意的是 非正常值浮点数 计算公式 与 正常值 不同:

V=(1)s×(0.frac)2×21BiasV=(-1)^s\times(0.frac)_2\times2^{1-Bias}

注意这里的:

  • 没有隐含的“1”,因为它是 非正规数
  • 指数固定为 1Bias1-Bias,而不是 expBiasexp-Bias,因为 阶码是全 0;
  • 这是为了在非常接近 0 的地方 保持精度连续性,即从最小正规数向 0 过渡的“填充区”。这种机制也称为渐进下溢。

下表总结了给出了 浮点数 表示各种情况的有效值计算:

类型 阶码(expexp 尾数(fraction) 有效值公式
正常值(规格化数) 非全 0、非全 1 ff (1)s(1.f)2×2expBias(-1)^s(1.f)_2\times2^{exp-Bias}
非正常值(非规格化数) 全 0 ff(必须非 0,否则是 0) (1)s(0.f)2×21Bias(-1)^s(0.f)_2\times2^{1-Bias}
±0\pm0 全 0 全 0 由符号位确定 +0+00-0
±\pm\infty 全 1 全 0 由符号位确定正、负无穷
NaN 全 1 非 0 Not a Number,表示非法或未定义结果

十进制字面值转 IEEE 754 编码

前文已经提到如果我们有 浮点数IEEE 二进制表示,如何将其转化为实际的 浮点数,就是通过如下的公式:

(1)s×(1.frac)2×2expBias(-1)^s\times(1.frac)_2\times2^{exp-Bias}

还有一种常见的考题是给定我们一个 浮点数字面值,比如 2.25,然后让我们去反推它的二进制表示,这里有没有什么简便的解法呢?

最简便的方法还是反向转换,即将一个 浮点数 表示为 一点几几( 1.frac )乘以二的多少次方( 2n )的格式,有这两部分可以分别计算出 阶码尾数,然后符号位由数字的正负判断。

2.25 为例,如果我们想得到该 浮点数单精度 表示:

2.25=1.125×21=98×21=(1+18)×21=(1+23)×212.25=1.125\times2^1=\frac{9}{8}\times2^1 =\left(1+\frac{1}{8}\right)\times2^1 =(1+2^{-3})\times2^1

由此可以计算出 浮点数 的各个部分:

  • 符号位为 0;
  • 阶码1+127=1281+127=128,二进制表示为 10000000
  • 尾数00100000000000000000000

所以 浮点数 的二进制为 0100 0000 0001 0000 ...,十六进制为 40100000H

对于有精度损失的情况,由于计算比较繁琐,更要完整写出中间步骤。其 尾数部分的计算与一般实数字面值转二进制相同:仍按前述“小数乘 2 取整”方法展开,再按目标精度进行舍入。

表示精度

上述的例子是一个比较理想的例子,2.25 可以精确地用 浮点数 表示。但在真实场景中,很多 实数 都是无法精确地用 浮点数 表示的。比如 1.2 这个数:

1.2=1+15=(1.001100110011)2×201.2=1+\frac{1}{5}=(1.001100110011\cdots)_2\times2^0

其中 15\frac{1}{5} 无法表示为有限个 2n2^{-n} 之和,所以对于这种情况,我们只能尽量地去接近这个数,即用有限位尾数近似。


若要理解如何接近这个数,我们首先要理解 精度 的概念。这里我们首先从简单的例子出发,假设 尾数只有 3 位,那么这些 尾数可以被精确表示为:

frac=i=13fi2i=k23,fi{0,1},k{0,1,,7}frac=\sum_{i=1}^{3} f_i2^{-i}=\frac{k}{2^3}, \qquad f_i\in\{0,1\},\quad k\in\{0,1,\ldots,7\}

在数轴中对应 [0, 1] 区间中的 8 个点:

三位尾数在零到一之间形成的八个离散可表示点(原资源 inline SVG 4)

如果一个尾数的大小与这些点都不相同的话,则需要找一个临近的点来近似,这也是导致 精度 丢失的原因:尾数的二进制表示法无法精确地表示 [0, 1] 中的每一个 实数,对于无法精确表示的,只能去近似。

但是这种误差可以随着 尾数 位数的增加而不断减小,比如对于 单精度浮点数 表示,尾数(frac)为 23 位,可以精确表示以下这些小数:

0,1223,2223,3223,,22312230,\frac{1}{2^{23}},\frac{2}{2^{23}},\frac{3}{2^{23}},\ldots, \frac{2^{23}-1}{2^{23}}

双精度浮点数 表示,尾数为 52 位,可以精确表示以下这些小数:

0,1252,2252,3252,,25212520,\frac{1}{2^{52}},\frac{2}{2^{52}},\frac{3}{2^{52}},\ldots, \frac{2^{52}-1}{2^{52}}

所以 尾数位数越多精度越高,用以近似表示某些 实数 时,误差更小。

舍入 是在数值计算中将一个数字转换为特定精度的过程。由于计算机中的 浮点数表示有限,许多数学运算结果不能完全精确地用 浮点数表示,因此需要 舍入来逼近这些结果。IEEE 754 的默认模式是“舍入到最近值;距离相同时取末位为偶数者”。

浮点数加减

复习提示(低优先级):如果深入掌握了 IEEE 浮点数表示的原理的话,那么掌握其过程也不是很难;重点掌握对阶、尾数运算、规格化、舍入以及溢出判断的先后顺序。

浮点数 加减计算过程包含以下几个步骤:对阶尾数加减尾数规格化

  1. 对阶:为了进行加减运算,两个 浮点数 必须具有相同的指数,这里采用低阶向高阶对齐的原则,过程如下:
    • 比较两个浮点数的 指数
    • 将较小指数的浮点数的 尾数右移,直到两个指数相等。
    • 右移过程中,需要注意尾数的 精度损失(尾数右移时可能会丢失低位精度)。
  2. 尾数加减:在指数对齐后,直接对两个 浮点数尾数进行加减。
    • 由于尾数已经对齐,可以直接进行 加减操作
    • 根据操作结果可能需要处理进位或借位。
  3. 尾数规格化:确保结果符合标准化浮点数的格式,即尾数以 1 开头。
    • 如果结果尾数不符合 1.xxxx 格式,则需要进行 规格化调整
    • 左移尾数并相应减少指数,或右移尾数并相应增加指数。
    • 规格化后,可能还需要进行 舍入,以符合尾数的位数限制。
      • 根据舍入模式(如“向偶数舍入”、“向零舍入”等)完成必要操作。

对阶尾数规格化 的过程中,由于可能存在 尾数右移,所以可能会导致 精度缺失。 因为 IEEE 浮点数尾数的位数是有限的,如果右移的过程中尾数中最右边的 1 被清除,就会导致 精度缺失

浮点加减中对阶、尾数运算、规格化与舍入的数据流程(原资源 inline SVG 6)

举一个实际的例子来说明一下,假设 A=1.625×23=(1.101)2×23A=1.625\times2^3=(1.101)_2\times2^3B=1.75×21=(1.11)2×21B=1.75\times2^1=(1.11)_2\times2^1。在计算 A+BA+B 时,使用以下步骤:

  1. 对阶:低阶向高阶对齐,B=(0.0111)2×23B=(0.0111)_2\times2^3
  2. 尾数相加:对齐后计算 A+B=[(1.101)2+(0.0111)2]×23=(10.0001)2×23A+B=[(1.101)_2+(0.0111)_2]\times2^3=(10.0001)_2\times2^3
  3. 尾数规格化:将 (10.0001)2×23(10.0001)_2\times2^3 规格化为 (1.00001)2×24(1.00001)_2\times2^4
  4. 舍入与溢出判断:按目标尾数位数舍入,再检查阶码是否上溢或下溢。本例无需舍入,也未溢出。
执行轨迹

浮点加法如何完成对阶、相加与规格化

逐步查看 A 与 B 的尾数和阶码怎样变化,并核对本例为何无需舍入且没有溢出。

01

写成规格化二进制当前状态:A=1.625×2^3=(1.101)₂×2^3,B=1.75×2^1=(1.11)₂×2^1;两数尚未改变。