Lecture 4:知识 - 浮点数的表示与运算
Chinese-language Computer Science 408 note: Lecture 4:知识 - 浮点数的表示与运算. Original source available where applicable.
On this page
This note is currently available in Chinese. The original wording, formulas and diagrams are preserved to avoid an unverified technical translation.
描述:浮点数以“尾数决定精度、阶码决定范围”为核心。其运算不是直接相加,而是对阶、尾数运算、规格化、舍入,并最终判断阶码是否溢出。
2.1 浮点表示与规格化
2.1.1 基本形式
浮点数一般写作:
$$X=(-1)^S\times M\times R^E$$
| 字段 | 含义 | 决定什么 |
|---|---|---|
| $S$ | 数符,取 0 或 1 | 正负 |
| $M$ | 尾数,通常是二进制定点小数 | 有效位数,即精度 |
| $E$ | 阶码/指数,通常以移码表示 | 小数点位置,即表示范围 |
| $R$ | 基数,常取 2、4 或 16 | 每调整 1 阶相当于缩放的倍率 |
- 浮点表示能显著扩大范围,但在相同字长下可表示的数的个数并没有增加,所以数轴分布更稀疏且不均匀,精度不如同字长定点数均匀。
- 规格化要求非零尾数的最高有效位尽量靠前。二进制规格化常写为 $1.xxxx\times2^E$,表示形式唯一并充分利用尾数字段。
- 左规:尾数最高位未出现有效
1时,尾数左移、阶码减 1,可连续多次;右规:尾数产生进位到小数点左边时,尾数右移、阶码加 1,通常只需一次。
2.1.2 IEEE 754
| 格式 | 字段 | 偏置值 | 规格化真值 |
|---|---|---|---|
| 单精度 float | 1 位符号 + 8 位阶码 + 23 位尾数 | 127 | $(-1)^S\times1.f\times2^{e-127}$ |
| 双精度 double | 1 位符号 + 11 位阶码 + 52 位尾数 | 1023 | $(-1)^S\times1.f\times2^{e-1023}$ |
- IEEE 754 的规格化二进制尾数首位恒为 1,故不存该位,称隐藏位。单精度的 23 位 fraction 实际提供 24 位有效二进制位。
- 阶码存储的是“真阶码 + 偏置”。例如单精度真阶码 $3$ 编为 $127+3=130=82_H$。
- 源 PDF 第 1 页把单精度偏置写成了 128,与其后页的公式和 IEEE 754 标准不符;实际偏置是 127,不能把这一处误印带入计算。
- 规格化有限数的最小正值和最大正值分别为:
| 格式 | 最小正规格化正数 | 最大有限正数 |
|---|---|---|
| 单精度 | $2^{-126}\approx1.1755\times10^{-38}$ | $(2-2^{-23})2^{127}=2^{128}-2^{104}\approx3.4028\times10^{38}$ |
| 双精度 | $2^{-1022}\approx2.2251\times10^{-308}$ | $(2-2^{-52})2^{1023}\approx1.7977\times10^{308}$ |
对应的有限负数范围关于 0 对称,但 0 与最小正规格化正数之间还有非规格化数。
原 PDF 的两道范围题
- 2012 年题:IEEE 754 单精度
float能表示的最大正整数是多少?选项为 A $2^{126}-2^{103}$、B $2^{127}-2^{104}$、C $2^{127}-2^{103}$、D $2^{128}-2^{104}$。最大有限数的阶码字段为 254,真阶码为 $254-127=127$;尾数字段全 1 时有效数为 $2-2^{-23}$,故最大值是 $(2-2^{-23})2^{127}=2^{128}-2^{104}$,选 D。这个值本身是整数;题目不是问“从 0 起每个整数都能连续精确表示到哪里”。 - 2018 年题:IEEE 754 单精度格式能表示的最小正规格化正数是多少?选项为 A $1.0\times2^{-126}$、B $1.0\times2^{-127}$、C $1.0\times2^{-128}$、D $1.0\times2^{-149}$。规格化数的最小阶码字段为 1,真阶码为 $1-127=-126$,fraction 全 0 时有效数为 1,故选 A;D 是最小正非规格化数。
| 阶码字段 | 尾数字段 | 含义 |
|---|---|---|
| 全 0 | 全 0 | $+0$ 或 $-0$ |
| 全 0 | 非 0 | 非规格化数,用于渐进下溢 |
| 1 至全 1 前 | 任意 | 规格化有限数,使用隐藏位 1 |
| 全 1 | 全 0 | 正/负无穷大 |
| 全 1 | 非 0 | NaN(非数/无定义数) |
- 单精度非规格化数的真值为 $(-1)^S\times0.f\times2^{-126}$,最小正非零值为 $2^{-149}$;双精度对应最小正非零值为 $2^{-1074}$。它们填补了 0 与各自最小正规格化数之间的间隙。
- “浮点范围大”不等于能精确表示所有大整数。单精度只有 24 位有效二进制精度,40 位整数即使数值没超范围,也可能无法精确表示;double 的有效尾数更多。
例:$-8.25$ 的 IEEE 754 单精度编码
源 PDF 的图示换算如下:
$$ -8.25_{10}=-1000.01_2=-1.00001_2\times2^3 $$
- 数符 $S=1$;
- 真阶码 $E=3$,阶码字段 $e=3+127=130=10000010_2$;
- 隐藏最高位 1 后,fraction 为
00001000000000000000000。
因此 32 位编码为:
1 | 10000010 | 00001000000000000000000
对应十六进制为 0xC1040000。
二进制小数与十进制小数
二进制小数从小数点后第一位起,位权依次为 $2^{-1},2^{-2},2^{-3},\ldots$。原 PDF 的两个示例为 $0.001_2=2^{-3}=0.125_{10}$,以及 $0.011_2=2^{-2}+2^{-3}=0.375_{10}$。反向将 $0.375_{10}$ 转为二进制,可依次乘以 2 并取整数位:$0.375\times2=0.75$ 取 0,$0.75\times2=1.5$ 取 1,$0.5\times2=1.0$ 取 1,得到 $0.011_2$。
2.2 浮点加减运算
2.2.1 四步过程
| 步骤 | 操作 | 关键点 |
|---|---|---|
| 对阶 | 小阶码向大阶码对齐 | 小阶码尾数右移,阶码加 1,直到相等;不会引起阶码上溢 |
| 尾数加减 | 按定点数规则进行 | IEEE 格式要先补回隐藏位 |
| 规格化 | 恢复 $1.xxx$ 形式 | 左规可多次,右规一次;右规使阶码加 1 |
| 舍入 | 砍掉超过精度的低位 | 最后才还原为 IEEE 字段,必要时可能再右规 |
- 对阶时右移丢出的低位不应立刻抛弃,应保留为保护位/舍入信息参加中间运算,否则会额外损失精度。
- 基数为 2 时,左规一次“尾数乘 2、阶码减 1”,右规一次“尾数除 2、阶码加 1”,数值本身不变。
- 尾数溢出本身不等于最终溢出:可通过右规将其恢复为规格化形式;真正的数值溢出主要看最终阶码是否超范围。
2.2.2 舍入方式
| 方法 | 规则 |
|---|---|
| 就近舍入、偶数优先 | 取最近可表示数;正好居中时使最后保留位为偶数 |
| 正向舍入 | 朝 $+\infty$ 取最近可表示数 |
| 负向舍入 | 朝 $-\infty$ 取最近可表示数 |
| 截断 | 直接丢弃低位;对正负数都趋向 0 |
例:若尾数保留小数点后两位,即保留 1.24 这样的三位有效数字,则 $1.2400\times10^5+0.0050\times10^5=1.2450\times10^5$ 恰在 $1.24\times10^5$ 与 $1.25\times10^5$ 中间,按偶数优先取 $1.24\times10^5$。
2.2.3 上溢、下溢与机器零
- 上溢:最终结果绝对值超过最大可表示有限数,正上溢与负上溢都需异常处理或相应溢出结果。
- 下溢(教材简化口径):规格化后的真阶码低于正常阶码下界。IEEE 754 可先用非规格化数实现渐进下溢;能被非规格化数精确表示的微小结果不必直接变成 0,严格的 IEEE 下溢异常还与舍入后是否不精确有关。
- 左规会使阶码减小,可能造成指数下溢;右规或舍入进位会使阶码加大,可能造成指数上溢。
- 机器零通常指尾数为 0,或在不支持/不采用非规格化数的简化模型中因阶码下溢而被冲刷为 0 的结果。非零但尚未规格化的尾数应先继续规格化,不能仅因“形式不规范”就判为机器零。
2.3 定点与浮点的比较、C 语言转换
| 维度 | 定点数 | 浮点数 |
|---|---|---|
| 数值范围 | 同字长较小 | 同字长远大于定点 |
| 精度分布 | 固定且均匀 | 相对精度近似固定,绝对间隔随数值变大 |
| 运算 | 直接整数/小数运算 | 须做阶码和尾数运算、规格化与舍入 |
| 溢出判断 | 结果超数值范围即溢出 | 主要看规格化后的阶码是否超范围 |
- C 中
float通常对应 IEEE 单精度,double对应双精度,long double的长度与格式由编译器和平台决定。 - “类型等级更高”不等于转换一定无损。
char -> int(当int能覆盖其范围)、int -> long(目标范围足够)和float -> double通常能保值;long -> double是否精确取决于整数位宽和 double 的有效精度,不能列为无条件无损转换。 - C 的混合表达式应按整数提升与通常算术转换逐步判断,而不是笼统地把“低类型”转为“高类型”。
int -> float在整数有效位超过 float 的 24 位精度时会舍入,常见 32 位int -> double则可精确容纳。 double -> float可能舍入、上溢或下溢;float/double -> int先截去小数部分(趋向 0),若截断后的值仍超出目标整数范围,C 语言层面属于未定义行为,不能简单当作普通回绕。
相关知识
- 科目导航:计算机组成原理导航
- 阅读顺序:上一篇:数制与编码 · 下一篇:运算方法和运算电路
2.4 本节复盘
- 尾数决定精度、阶码决定范围;IEEE 规格化数用隐藏首位 1 扩展有效精度。
- 单精度公式为 $(-1)^S\times1.f\times2^{e-127}$,双精度偏置为 1023;全零/全一阶码有专门语义。
- 浮点加减必须依次对阶、尾数运算、规格化、舍入;小阶码向大阶码对齐,右移低位需保留舍入信息。
- 结果是否真正溢出取决于阶码;尾数溢出可经右规修正。大范围不代表大整数一定能精确表示。