4. 定点数运算:补码加减法、溢出判断、原码一位乘法、补码一位乘法、原码除法、补码除法(恢复余数法、不恢复余数法)
定点数运算是计算机组成原理中运算器的核心基础。计算机中的定点数通常以补码形式存储和参与运算,因为补码可以将符号位与数值位统一处理,使得加减法运算规则统一、电路实现简单。本章围绕定点数的加减乘除四类基本运算展开,重点讲解补码加减法的规则与溢出判断方法、原码/补码一位乘法的迭代流程,以及原码/补码除法中恢复余数法与不恢复余数法的算法思想。掌握这些内容,不仅有助于理解ALU(算术逻辑单元)的硬件设计,也是后续学习浮点数运算、流水线冲突处理等章节的重要前提。
4.1 补码加减法运算
4.1.1 核心原理
补码加减法的理论基础是模运算。对于n位定点整数(含符号位),补码的定义为:正数的补码等于其原码本身;负数的补码等于其值按位取反后加1。补码的最大优势在于:减法可以转化为加法,即 [X−Y]补 = [X]补 + [−Y]补,其中 [−Y]补 是对 [Y]补 连同符号位一起取反加1得到。这样,运算器内部只需要一套加法器电路,无需单独设计减法器,大大简化了硬件结构。
运算时,符号位与数值位一同参与二进制加法,最高位(符号位)产生的进位自然丢弃(模2ⁿ意义下)。例如,4位补码中,3 + (−2) = 0011 + 1110 = 10001,丢弃最高位进位后得到 0001,即正确结果1。
[X+Y]补 = ([X]补 + [Y]补) mod 2ⁿ
[X−Y]补 = ([X]补 + [−Y]补) mod 2ⁿ
[−Y]补 = ¬[Y]补 + 1(按位取反加1,包括符号位)
4.1.2 溢出判断方法
溢出是指运算结果超出了机器字长所能表示的范围。对于n位补码,可表示范围为 [−2ⁿ⁻¹, 2ⁿ⁻¹−1]。当两个同号数相加或两个异号数相减时,可能产生溢出。常用的溢出判断方法有三种:
| 判断方法 | 实现原理 | 判断条件 |
|---|---|---|
| 双符号位法(变形补码) | 运算时采用两个符号位 S₁S₂ | S₁S₂ = 01 表示正溢出;S₁S₂ = 10 表示负溢出;S₁S₂ = 00 或 11 表示无溢出 |
| 进位位异或法 | 比较符号位进位 C₁ 与最高数值位进位 C₂ | C₁ ⊕ C₂ = 1 时发生溢出 |
| 符号位与数值位最高位判断 | 观察运算前后符号位变化 | 两正数相加结果为负,或两负数相加结果为正,则溢出 |
双符号位法在硬件实现上最为直观:将补码的符号位复制一份,形成 S₁S₂ 两位,运算结果中若 S₁S₂ 不一致,则说明发生了溢出。实际存储时只保留一位符号位,双符号位仅用于运算过程中的检测。
4.1.3 案例拆解
场景:4位补码系统(范围 −8~7),计算 5 + 4。
[5]补 = 0101
[4]补 = 0100
0101
+ 0100
------
1001 → 符号位为1,结果为负数,明显错误!
双符号位验证:00 0101 + 00 0100 = 00 1001 → S₁S₂=00,无溢出?
等等,这里需要仔细分析。4位补码中,5+4=9,已经超出最大值7,属于正溢出。使用双符号位法:00 0101 + 00 0100 = 00 1001,S₁S₂=00,看似无溢出,但结果 1001 是 −7 的补码,显然错误。问题出在哪里?
实际上,双符号位法要求使用变形补码,即符号位扩展为两位后参与运算。正确的做法是:5 的变形补码为 00 0101,4 的变形补码为 00 0100,相加得 00 1001,此时 S₁S₂=00,但数值位最高位(第3位)为1,与符号位不同,说明结果超出了4位原码的表示范围。因此,双符号位法需要结合数值位最高位判断:当 S₁S₂=00 但最高数值位为1时,表示正溢出;当 S₁S₂=11 但最高数值位为0时,表示负溢出。
更严谨的双符号位判断规则是:S₁S₂=01 正溢出,S₁S₂=10 负溢出,S₁S₂=00 且最高数值位=0 无溢出,S₁S₂=00 且最高数值位=1 正溢出,S₁S₂=11 且最高数值位=1 无溢出,S₁S₂=11 且最高数值位=0 负溢出。 实际上,最简洁的判断方式是使用进位位异或法:符号位进位 C₁ 与最高数值位进位 C₂ 异或,若为1则溢出。本例中,C₁=0(符号位无进位),C₂=1(最高数值位有进位),0⊕1=1,判定溢出,正确。
4.2 原码一位乘法
4.2.1 核心原理
原码一位乘法的基本思想是:符号位单独处理,数值位取值相乘。设被乘数 X 的原码为 XₛX₁X₂...Xₙ,乘数 Y 的原码为 YₛY₁Y₂...Yₙ,则乘积的符号位 Zₛ = Xₛ ⊕ Yₛ,乘积的数值部分为 |X| × |Y| 的值相乘结果。
值相乘采用“移位相加”的迭代方式:从乘数的最低位开始,逐位检查乘数的每一位。若当前位为1,则将被乘数加到部分积上;若为0,则不加。每次操作后,部分积右移一位,相当于乘数位权递减。经过n次迭代后,得到2n位的乘积数值部分。
4.2.2 操作流程
- 初始化:部分积 P = 0,乘数 Y 放入乘数寄存器,被乘数 X 放入被乘数寄存器。
- 从乘数最低位 Yₙ 开始,检查 Yₙ 的值:若 Yₙ=1,则 P = P + |X|;若 Yₙ=0,则 P 不变。
- P 和 Y 联合右移一位(逻辑右移),乘数最低位移出,部分积最低位移入乘数最高位。
- 重复步骤2-3共n次。
- 最终结果 = 符号位异或值 + P和Y拼接的2n位数值。
4.2.3 案例拆解
场景:4位原码乘法,X = −3,Y = +5,机器字长4位(1位符号位+3位数值位)。
|X| = 011,|Y| = 101
符号位:Zₛ = 1 ⊕ 0 = 1(结果为负)
迭代过程(部分积P初始为000,乘数Y=101):
第1次:Y最低位=1 → P = 000 + 011 = 011 → 右移 → P=001,Y=110(移入1)
第2次:Y最低位=0 → P不变=001 → 右移 → P=000,Y=110(移入1)
第3次:Y最低位=1 → P = 000 + 011 = 011 → 右移 → P=001,Y=101(移入1)
最终:P=001,Y=101,拼接为 001101 = 13(十进制)
结果:−13,验证:−3 × 5 = −15?不对!
这里发现结果不对。仔细检查:4位原码中,3位数值位最大表示7,−3×5=−15,超出了4位原码的表示范围(−7~7)。因此,这个例子本身就有问题。正确的做法是:当乘积可能超出单字长时,应使用双字长存储。对于4位原码乘法,结果需要8位(1位符号位+7位数值位)才能完整表示。实际考试中,通常给定n位原码,要求计算2n位乘积。
修正案例:设 X = −3(原码 1011),Y = +2(原码 0010),使用4位原码乘法,结果用8位表示。
|X| = 011,|Y| = 010
符号位:Zₛ = 1 ⊕ 0 = 1
迭代(P初始000,Y=010):
第1次:Y最低位=0 → P不变=000 → 右移 → P=000,Y=001(移入0)
第2次:Y最低位=1 → P=000+011=011 → 右移 → P=001,Y=100(移入1)
第3次:Y最低位=0 → P不变=001 → 右移 → P=000,Y=100(移入1)
最终:P=000,Y=100,拼接为 000100 = 4
结果:−4,验证:−3×2=−6?还是不对!
再次发现问题。实际上,原码一位乘法中,部分积和乘数联合右移时,乘数寄存器需要扩展一位用于接收部分积的最低位。正确的迭代过程应为:
设P为3位,Y为3位,联合寄存器为6位(P在前,Y在后)
初始:P=000,Y=010 → 联合=000010
第1次:Y最低位=0 → P不变 → 联合右移 → 000001
第2次:Y最低位=1 → P=000+011=011 → 联合=011001 → 右移 → 001100
第3次:Y最低位=0 → P不变 → 联合右移 → 000110
最终联合=000110 = 6,符号位为1,结果−6 ✓
关键点在于:每次右移时,部分积的最低位移入乘数寄存器的最高位,而乘数的最低位被丢弃。 这样经过n次迭代后,乘数寄存器中保存的是乘积的低n位,部分积保存的是高n位。
4.3 补码一位乘法(Booth算法)
4.3.1 核心原理
补码一位乘法(Booth算法)的核心思想是:符号位直接参与运算,不需要单独处理符号。算法基于补码的数学性质,通过检查乘数末位和附加位(乘数最低位右侧额外增加的一位,初始为0)的组合来决定操作:
- YₙYₙ₊₁ = 00 或 11:部分积不变,右移一位
- YₙYₙ₊₁ = 01:部分积加被乘数 [X]补,右移一位
- YₙYₙ₊₁ = 10:部分积减被乘数(加 [−X]补),右移一位
这里的 Yₙ 是乘数当前最低位,Yₙ₊₁ 是附加位(初始为0)。每次操作后,部分积和乘数联合右移一位(算术右移,符号位保持不变)。经过n次迭代后,再额外右移一次(共n+1次),得到2n位的乘积。
4.3.2 操作流程
- 初始化:部分积 P = 0,乘数 Y 放入乘数寄存器,附加位 Yₙ₊₁ = 0,被乘数 X 的补码放入被乘数寄存器。
- 检查 YₙYₙ₊₁ 的组合,执行相应操作(加X、减X或不变)。
- 部分积和乘数联合算术右移一位(符号位扩展)。
- 重复步骤2-3共n次。
- 最后一次迭代后,不再右移(或再右移一次,取决于具体实现),乘积 = P拼接Y。
4.3.3 案例拆解
场景:4位补码乘法,X = −3(补码 1101),Y = +2(补码 0010),计算 X×Y。
[X]补 = 1101,[−X]补 = 0011
初始:P = 0000,Y = 0010,附加位 = 0
第1次:YₙYₙ₊₁ = 00 → P不变 → 算术右移 → P=0000,Y=0001,附加位=0
第2次:YₙYₙ₊₁ = 10 → P = P + [−X]补 = 0000+0011=0011 → 右移 → P=0001,Y=1000,附加位=1
第3次:YₙYₙ₊₁ = 01 → P = P + [X]补 = 0001+1101=1110 → 右移 → P=1111,Y=1100,附加位=0
第4次:YₙYₙ₊₁ = 00 → P不变 → 右移 → P=1111,Y=1110,附加位=0
最终:P=1111,Y=1110,拼接为 11111110 = −6(补码)✓
验证:−3 × 2 = −6,结果正确。注意算术右移时,符号位保持不变,即负数右移时高位补1。
4.4 原码除法
4.4.1 核心原理
原码除法的基本思想与手算除法类似:符号位单独处理(异或),数值位值相除。每次迭代中,用部分余数减去除数,若够减则商1,若不够减则商0。根据余数恢复策略的不同,分为恢复余数法和不恢复余数法(加减交替法)。
4.4.2 恢复余数法
恢复余数法的流程:
- 初始化:余数 R = 被除数值,商 Q = 0,除数 D 放入除数寄存器。
- 将 R 左移一位(相当于乘以2)。
- R = R − D。
- 若 R ≥ 0(符号位为0),则商上1;若 R < 0(符号位为1),则商上0,并恢复余数:R = R + D。
- 重复步骤2-4共n次。
- 最终余数可能需要修正(若最后一次恢复余数,则余数正确;否则需额外处理)。
恢复余数法的缺点是:当不够减时,需要额外执行一次加法来恢复余数,增加了运算次数和硬件复杂度。
4.4.3 不恢复余数法(加减交替法)
不恢复余数法是对恢复余数法的改进,其核心思想是:当余数为负时,不恢复余数,而是根据余数的符号决定下一步操作。
- 若余数 R ≥ 0:商上1,下一步 R = 2R − D(左移一位后减除数)
- 若余数 R < 0:商上0,下一步 R = 2R + D(左移一位后加除数)
这样,每一步只需一次加法或减法操作,不需要恢复余数,效率更高。
4.4.4 案例拆解
场景:4位原码除法,X = +7(原码 0111),Y = +3(原码 0011),计算 X÷Y。
|X| = 0111,|Y| = 0011,符号位:0⊕0=0(结果为正)
恢复余数法:
初始:R=0111,Q=0000
第1次:R左移=1110,R−D=1110−0011=1011(负数)→ 商0,恢复R=1110+0011=0001(注意:这里R左移后减D,恢复后R=0001?)
这里需要仔细理解。恢复余数法中,每次迭代先左移再减除数。第1次:R=0111,左移后为1110(注意这是4位,实际应扩展到5位以容纳进位)。更严谨的做法是使用双倍字长寄存器。为简化说明,我们使用5位寄存器:
初始:R=00111(5位),D=00011
第1次:R左移=01110,R−D=01110−00011=01011(正)→ 商1
第2次:R左移=10110,R−D=10110−00011=10011(负)→ 商0,恢复R=10011+00011=10110
第3次:R左移=01100,R−D=01100−00011=01001(正)→ 商1
第4次:R左移=10010,R−D=10010−00011=01111(正)→ 商1
商Q=1011(二进制)=11?不对,7÷3=2余1
问题出在商的位置。实际上,商应该从最高位开始上,每次上商后左移。正确的流程是:商寄存器初始为0,每次上商后,商左移一位,将新的商位放入最低位。经过4次迭代后,商为0010(十进制2),余数为0001(十进制1)。
4.5 补码除法
4.5.1 核心原理
补码除法与补码乘法类似,符号位自然参与运算,不需要单独处理。补码除法的基本思想是:根据被除数和除数的符号关系,决定每一步是加除数还是减除数。具体规则如下:
- 若被除数与除数同号:余数减去除数(R = R − D)
- 若被除数与除数异号:余数加上除数(R = R + D)
- 若余数与除数同号:商上1,下一步余数左移后减除数
- 若余数与除数异号:商上0,下一步余数左移后加除数
补码除法的商以补码形式产生,最后需要根据余数的符号进行修正:若被除数与除数异号,则商需要加1修正(即商的最低位加1);若余数与被除数异号,则余数需要加除数修正。
4.5.2 操作流程
- 初始化:余数 R = 被除数补码,除数 D = 除数补码,商 Q = 0。
- 根据 R 与 D 的符号关系,执行 R = R − D 或 R = R + D。
- 判断 R 与 D 是否同号:同号则商上1,异号则商上0。
- R 和 Q 联合左移一位(算术左移)。
- 重复步骤2-4共n次。
- 修正:若被除数与除数异号,则商加1;若余数与被除数异号,则余数加除数。
4.5.3 案例拆解
场景:4位补码除法,X = −7(补码 1001),Y = +3(补码 0011),计算 X÷Y。
被除数X=1001,除数D=0011
X与D异号 → 初始操作:R = R + D = 1001 + 0011 = 1100
第1次:R=1100,D=0011,R与D异号 → 商0,左移 → R=1000,Q=0000
第2次:R与D异号 → R = R + D = 1000+0011=1011,R与D异号 → 商0,左移 → R=0110,Q=0000
第3次:R与D同号 → R = R − D = 0110−0011=0011,R与D同号 → 商1,左移 → R=0110,Q=0001
第4次:R与D同号 → R = R − D = 0110−0011=0011,R与D同号 → 商1,左移 → R=0110,Q=0011
修正:被除数与除数异号 → 商加1:Q=0011+0001=0100(补码,表示−4?)
验证:−7÷3 = −2余