数的表示与运算
位串解释、类型转换、溢出判断与 IEEE754 题的固定动作:先定位数和范围,再把每条 C 语句翻成位串。
以这一支为主线的综合题有三道:2011#43、2017#43、2020#43,都是「给一小段 C 代码,问机器数、问值、问会不会溢出」。此外 2013#44、2022#43、2024#43 的标志位小问和 2025#44 的除法器也落在这一支上。
知识框架概要
一、位串没有意义,解释方式才有
- 同一个位串可以被解释成三种东西:无符号数、补码、IEEE754 浮点数,见 整数的表示 和 IEEE 浮点数表示
- n 位补码范围 ,n 位无符号范围
- 类型转换只做两件事之一:改解释(同长度 signed ↔ unsigned,位串一个不动)或改长度(变长时零扩展 / 符号扩展,变短时直接截断),见 有符号整数和无符号整数、不同长度的类型转换
- 混合运算里 unsigned 会把 int 拉过去,比较也跟着变成无符号比较——2017#43 第 1 问的死循环就是这一条
二、运算电路和标志位
- 加减共用一个加法器:减法先把减数各位取反、末位加 1,再当成加法送进去。所以无符号加减和带符号加减走的是同一套电路,区别只在事后怎么读这串结果位,见 加法运算电路、减法运算电路
- 四个标志位就是这次加法顺手引出来的四根线,每根只回答一个问题:结果是否为 0(
ZF)、结果最高位是什么(SF)、最高位向外有没有进位或借位(CF)、最高两位的进位是否不相等(OF)。四个同时产生,但CF只在把结果读成无符号数时有意义,OF只在读成带符号数时有意义,见 条件标志 - 乘法是「加一次、右移一位」重复 n 遍,所以 n 位乘 n 位的完整结果有 2n 位;把这 n 遍摊开成一片电路就是阵列乘法器——快,但面积大。真题只问两件事:哪种实现更快,以及只保留低 n 位会不会丢掉信息。见 乘法运算电路
- 除法是「减一次、左移一位」重复 n 遍,因此有两种失败方式:除数为 0,以及商放不进目标寄存器(商溢出)。这是两个不同的异常,不能合成一句,见 除法运算电路
- 乘除 2ᵏ 可以直接用移位代替(左移 k 位、算术右移 k 位),但负数算术右移是向下取整,和 C 语言除法的向零取整不一致——被问「能不能用移位实现」时这一句必须交代
三、IEEE754 单精度先把 32 位切成 1 + 8 + 23,其余全是这一刀的推论
- 切法与偏置:
符号 1 位 ‖ 阶码 8 位 ‖ 尾数 23 位,阶码里存的是「真实指数 + 127」,规格化数的尾数前面还隐含一个 1,见 IEEE 浮点数表示 - 尾数那一刀给出有效位 24 位(隐含的 1 加 23 位尾数),这是判断「能不能精确表示」的唯一依据:需要超过 24 个有效二进制位的数一定要舍入
- 阶码那一刀给出「溢出在哪」:8 位阶码里可用的只有 1~254(真实指数 −126~127),全 0 与全 1 留作特殊值——全 1 且尾数全 0 是 ±∞(
+∞的机器数就是7F800000H),全 1 且尾数非 0 是 NaN,见 异常值 - 所以判浮点溢出永远只看规格化和舍入之后的阶码有没有越界;尾数在运算中溢出只会导致尾数右移、阶码加 1,它本身不是溢出,见 溢出判断
- 浮点加减固定五步:对阶(小阶的尾数右移,向大阶靠)→ 尾数加减 → 规格化 → 舍入 → 判阶码溢出,见 浮点数加减
解题流程模板
这一支的四步是真有先后的:位数和范围不先定下来,后面每一步的结论都会跟着变。
第一步:先写位数和范围
在草稿最上面写死三行:字长几位、每个变量是什么类型、这些类型的取值范围。2011#43 是 8 位机,unsigned 范围 0~255、int 范围 −128~127,题里的 134 和 246 都超出了 int 范围——这一行写下来,后面四个小问全部有了依据。
第二步:每条 C 语句翻成两行
位串: 运算按位算出来的结果(十六进制,位数补齐)
解释: 这个位串按该变量的类型读出来是多少
位串只算一次,解释按类型各读一次。int m = x; 这类赋值只改解释不改位串;x - y 这种无符号减法照样按补码加法算,只是溢出判据换成 CF。
第三步:溢出判断按类型对号入座
| 运算 | 判据 |
|---|---|
| 无符号加 | 最高位向外有进位(CF = 1) |
| 无符号减 | 有借位,即被减数小于减数 |
| 带符号加减 | 最高两位进位不相等(OF = Cn ⊕ Cn−1),或「两正得负、两负得正」 |
| 乘法只取低 n 位 | 无符号:高 n 位不全为 0;带符号:高 n 位不是低 n 位的符号扩展 |
| 浮点 | 只看规格化舍入后的阶码是否越界,尾数溢出不算 |
2020#43 第 4 问就是倒数第二行:n = 32、x = 2³¹−1、y = 2 时,umul 不溢出而 imul 溢出。
第四步:机器数写成补齐的十六进制
8 位写两个十六进制位,32 位写八个,末尾统一带 H。2017#43 要求同时给出 f1(23) 的 00FFFFFFH 和 f2(23) 的 4B7FFFFFH——同一个数值的两种编码要分别写清阶码怎么来的、尾数怎么来的。
浮点数的两条上限单独记
同一道题的第 4、5 问是这一支最典型的四问连环,答案全部由「有效位 24 位」和「阶码上限」两句话推出:
f1(n)与f(n)相等的最大 n 是 30(int 最大值是 2³¹−1);f2(n)结果精确的最大 n 是 23(f(23) 恰好 24 个 1,不需舍入);f2(n)不溢出的最大 n 是 126(再大一档舍入后阶码就到 255)。
历年题索引
| 真题 | 主线 | 这一年特别问了什么 |
|---|---|---|
| 2011#43 | 8 位机的无符号与补码加减 | 四种加减能否共用一个加法器、哪条语句溢出 |
| 2013#44 | 条件转移 + 标志寄存器 | 无符号数「≤」转移要用哪几个标志位 |
| 2017#43 | int 与 float 计算同一个 f(n) | 死循环原因、机器数、精确与溢出的最大 n |
| 2020#43 | 乘法的三种实现 | 执行时间长短比较、取低 n 位的溢出判断 |
| 2022#43 | 数据通路 + 标志位 | 写出 SF 与 OF 的逻辑表达式 |
| 2024#43 | 控制信号 + ALU 标志 | slli 与 lw 的 Ext 和 ALUctr 取值 |
| 2025#44 | 补码除法器数据通路 | 除法异常的两种情形与异常响应中 CPU 的动作 |
易错清单
- 同长度的类型转换以为位串会变。
int m = x;位串完全不动,变的只是怎么读它。 - 无符号比较当成带符号比较。只要有一个操作数是 unsigned,整个比较就是无符号的。
- 用 CF 判带符号溢出。CF 和 OF 是两套判据,2013#44 专门考这个边界。
- 浮点数以为 32 位就能精确表示 32 位整数。有效位只有 24 位。
- 尾数溢出就下结论「结果溢出」。要等规格化和舍入之后再看阶码。
- 机器数少写位数。8 位就补足两位十六进制,不能只写有效数字。