浮点数的表示
定点数把小数点钉死在一个约定位置上,于是范围和精度成了一对不可调和的矛盾:小数点靠左则精度高、范围小,靠右则范围大、精度低,
浮点数的思路是把小数点的位置也编进数里。同样是 32 位,浮点数用一部分位记”小数点在哪”(阶码),剩下的记有效数字(尾数)。可表示的值仍然只有
机制
一般格式
:数符,1 位。 :尾数,定点小数,决定精度。 :阶码,定点整数,决定范围。 :基数,隐含约定,IEEE 754 中 。
阶码和尾数的位数分配是一个纯粹的权衡:在总位数固定时,阶码每多一位,范围按平方级扩大;尾数每多一位,精度翻一倍。所以 float 到 double 时阶码只从 8 位加到 11 位,尾数却从 23 位加到 52 位——范围已经够用了,精度才是瓶颈。
规格化:为什么必须有
同一个数有无数种写法:
规格化就是那条约束:要求尾数的最高有效位必须是有意义的。二进制下
- 尾数用原码表示时,规格化形式是
(小数点后第一位为 1)。 - IEEE 754 采用另一种约定:
(小数点前一位为 1)。
规格化还有一个实际收益,它是 IEEE 754 隐藏位的来源:既然规格化后最高位必然是 1,那这一位就不必存储了——用同样的位数多得到一位精度。这一位叫隐藏位。
IEEE 754 的字段划分
flowchart LR subgraph SP["单精度 float(32 位)"] direction LR S1["S<br/><b>1 位</b>"]:::s E1["阶码 E<br/><b>8 位</b>(偏置 127)"]:::e M1["尾数 M<br/><b>23 位</b>(+1 位隐藏位)"]:::m end subgraph DP["双精度 double(64 位)"] direction LR S2["S<br/><b>1 位</b>"]:::s E2["阶码 E<br/><b>11 位</b>(偏置 1023)"]:::e M2["尾数 M<br/><b>52 位</b>(+1 位隐藏位)"]:::m end SP ~~~ DP classDef s fill:#fee2e2,stroke:#dc2626,color:#7f1d1d classDef e fill:#fef3c7,stroke:#d97706,color:#78350f classDef m fill:#dbeafe,stroke:#2563eb,color:#1e3a5f
字段顺序是
阶码的偏置为什么是
IEEE 754 的阶码是移码,但偏置取
原因是阶码的全 0 和全 1 被保留作特殊用途,真正可用的阶码字段值是
偏置取 127 而不是 128,让指数范围向正侧偏一格,收益很具体:最小正规格化数的倒数不会上溢。
五类编码
阶码字段的取值决定了这串位属于哪一类,五类的规则互不相同,套错公式是最常见的错误:
| 阶码 E | 尾数 M | 类别 | 值 |
|---|---|---|---|
| 任意 | 规格化数 | ||
| 全 0 | 非规格化数 | ||
| 全 0 | |||
| 全 1 | |||
| 全 1 | NaN | 非数 |
(表中数值为单精度;双精度把
非规格化数的两个反直觉之处,都要单独记:
① 隐藏位是 0 不是 1。 阶码全 0 时不再假定
② 实际指数是
非规格化数存在的意义叫逐级下溢:如果没有它,比 a != b 却 a - b == 0 的怪事会发生。有了非规格化数,
关键边界值
这几个值建议直接记住,真题里反复出现:
| 单精度 float | 位串 | 值 |
|---|---|---|
| 最小正非规格化数 | 0x00000001 | |
| 最大非规格化数 | 0x007FFFFF | |
| 最小正规格化数 | 0x00800000 | |
0x3F800000 | 阶码 | |
| 最大规格化数 | 0x7F7FFFFF | |
0x7F800000 | ||
| NaN | 0x7FC00000 等 |
| 双精度 double | 值 |
|---|---|
| 最小正规格化数 | |
| 最大规格化数 | |
| 最小正非规格化数 |
有效位数:单精度
编码与解码模板
十进制 → IEEE 754(四步):
- 转二进制:整数部分除 2 取余,小数部分乘 2 取整(2.1.1)。
- 规格化成
:小数点移到最高位 1 的右边,记下移动量 。 - 算阶码字段
,转成 8 位二进制。 - 取尾数字段:
中小数点后的 23 位,不足补 0,超出要舍入。
例:1000 0010,1001 后补 19 个 0。位串 1100 0001 0100 1000 0000 0000 0000 0000 0xC1480000。
IEEE 754 → 十进制(三步):
- 按 1/8/23 拆字段。
- 先看阶码属于五类中的哪一类——这一步绝不能跳过。
- 规格化数:
。
边界
IEEE 754 的移码与教材移码的偏置不同
| 偏置 | 阶码全 0 表示 | |
|---|---|---|
| 教材标准移码 | 最小指数 | |
| IEEE 754 阶码 | 保留:非规格化数或 0 |
题目问”IEEE 754 的阶码用什么码”,答案是移码;问”偏置是多少”,答案是 127 而不是 128。 两问经常连着出现。
规格化 ≠ 精确
规格化只是编码唯一化的要求,与这个数能否被精确表示毫无关系。
“浮点数不精确”的根源在2.1.1 的进制转换,不在 IEEE 754 的位数分配。 增加尾数位只能减小误差,永远不能消除。
阶码决定范围,尾数决定精度——但都不是唯一因素
这句常见结论需要加限定:
- 范围主要由阶码位数决定,但尾数也有微小贡献(最大值是
而非 )。 - 精度由尾数位数决定,且这是相对精度:绝对精度随数值大小变化,
附近相邻两个可表示数相差 。
“浮点数在数轴上不等距”这一点是理解一切浮点问题的钥匙,包括为什么大数加小数会”吃掉”小数(见 2.3.2)。
零、无穷、NaN 的性质
| 特点 | |
|---|---|
| 有两个零编码,但比较时 | |
| 由上溢或非零数除以 0 产生; | |
| NaN | 由 |
”
同一位串既是浮点数也是整数
0x3F800000 作为 float 是 int 是
因此 (int)f 与 *(int*)&f 结果完全不同——前者是值转换,后者是位型重解释,见 2.1.4、2.3.3。
对照速查
| float | double | |
|---|---|---|
| 总位数 | 32 | 64 |
| 阶码位数 / 偏置 | 8 / 127 | 11 / 1023 |
| 尾数字段 / 有效位 | 23 / 24 | 52 / 53 |
| 十进制有效数字 | 约 7 位 | 约 15~16 位 |
| 最小正规格化数 | ||
| 最大规格化数 |
| 阶码 | 尾数 | 类别 |
|---|---|---|
| 全 0 | ||
| 全 0 | 非规格化数( | |
| 其余 | 任意 | 规格化数( |
| 全 1 | ||
| 全 1 | NaN |
考点
- 阶码决定范围、尾数决定精度;
double主要加的是尾数 - 规格化的目的是编码唯一,隐藏位是它的副产品,白得一位精度
- IEEE 754 阶码用移码,偏置是
(127/1023),不是 - 偏置取 127 使最小规格化数的倒数不上溢
- 阶码全 0 时隐藏位为 0,实际指数取
而不是 - 非规格化数实现逐级下溢,代价是精度递减
- 五类编码必须先判类别再套公式
; - 单精度约 7 位十进制有效数字,双精度约 15~16 位
- 字段顺序
使同号浮点可按位串比较大小
链接
- 🏠 返回总览:计算机组成原理第 2 章:数据的表示和运算总览
- ⬅️ 上一节:2.2.4 定点数的乘除运算
- ➡️ 下一节:2.3.2 浮点数的加减运算
- 📖 名词库:第 2 章名词库