计算机组成原理这门课,不少院校用的都是微课版教材,第三章节“运算方法与运算器”可以说是整门课的分水岭——前面的进制转换、真值表示还属于热身,到了这一章,补码运算、溢出判断、乘法除法器、ALU设计一股脑全来了。很多同学在这章开始掉队,不是因为内容有多高深,而是习题答案往往只给一个最终结果,中间推导过程一笔带过,你对着答案都不知道自己错在哪一步。
这篇内容我把第三章课后习题里最高频、最核心的几类题型重新梳理了一遍,每题都给出完整推导过程和关键踩坑点,覆盖补码加减、溢出判断、移位运算、乘除法实现、ALU功能表分析这几大块。适合正在学运算器章节的本科生,也适合期末突击和考研复习第一轮的同学参考。
1. 第三章核心框架与复习主线
1.1 运算方法章节的知识地图
在做题之前,先把这一章的知识结构在脑子里搭起来。运算器部分看上去题目类型多,但你归纳下来就三条主线:
第一条主线是“数的机器表示”。原码、反码、补码、移码之间的转换,这是整章的底层地基。我见过太多同学补码加减法算错,追根溯源不是加法不会,而是真值转补码、补码转真值这一步出了岔子。这里有一个必须刻进DNA的规则:正数的原码=反码=补码,负数的补码是原码除符号位外取反加1。而补码转回真值时,如果你不记得“符号位不变、按位取反再加1”这个逆过程,也可以用更不容易错的判断法——看补码符号位是1,说明是负数,直接对这个补码再取反加1就得到原码的数值部分。
第二条主线是“定点数的四则运算”。加减法考察补码运算和溢出判断,乘除法考察原码一位乘法、补码一位乘法(Booth算法)、原码加减交替除法。这部分是习题集的绝对主力,也是期末考试大题的高发区。
第三条主线是“运算器的硬件实现”。包括加法器的进位链设计(串行、并行、组间串行进位)、ALU芯片的功能表解读、标志位(ZF、OF、SF、CF)的产生逻辑。这类题目偏设计,需要你把前两条主线的数学逻辑转换成电路思维。
做课后题时,我强烈建议你先不看答案,按照这三条主线给每道题归类。归类的过程就是建立知识树的过程,比单纯刷题效率高得多。
1.2 课后习题的类型分布与考察重点
微课版第三章的课后题,整体分布是有规律的。我统计过近几届学生反馈的常见题型,大致分成五类:
第一类是进制转换与真值表示题,占比不高,但几乎每章必考,属于送分题,但送分题最容易因为粗心丢分。
第二类是补码加减运算与溢出判断,这是最核心的基础题。变形常考双符号位判断溢出、单符号位判断溢出、以及结合硬件标志位判断结果是否有效。
第三类是移位运算与舍入处理。算术移位和逻辑移位的区别,移位与乘除2的幂次之间的关系,这些都是高频考点。
第四类是定点乘除法运算。原码一位乘法器的手算流程、补码一位乘法的校正法、Booth算法的运算表,这类题分值高、过程长,是最容易在细节上出错的。
第五类是ALU与加法器设计。像74181的功能表分析、组间串行进位的延迟计算,这类题目考察你对硬件结构的理解深度。
2. 各类题型的通用解法与底层原理
2.1 进制转换与真值表示:一切运算的地基
进制转换虽然基础,但在题目里往往不是直接考“把十进制转二进制”这么简单,而是藏在数值范围求解、机器字长判断这些题里。比如问:某机器字长8位,用补码表示定点整数,能表示的最小负数是好多?看似是填空题,实际考的是补码的表示范围。
这类题的通用解法是记牢公式:补码定点整数n位的表示范围是-2^(n-1)到2^(n-1)-1。8位就是-128到127。很多人不知道为什么最小负数是-128而不是-127,根源在于补码的0只有一种表示,多出来的编码给了最左边的那个负数。补码-128的二进制表示是1000 0000,你用“原码取反加1”是算不出这个数的,因为它的原码在8位里不存在。这个特例一定要单独记。
转换题的实操技巧,我推荐“分组转换法”做二进制和十六进制的互转,四位一组,从二进制小数点开始分别往两边分。转十进制时用“权值展开法”,注意别把符号位当成数值位一起展开。
2.2 补码加减运算与溢出判断:最容易被扣分的环节
补码加减运算有一个让人又爱又恨的性质:减法可以统一成加法来做。但天道好轮回,统一的过程里,符号位参与运算带来一个致命问题——溢出。
溢出判断的通用方法有三种,但我建议考试时优先用双符号位法(也叫变形补码法),因为它的判断标准最直观:运算结果的两个符号位不一致就是溢出。01表示正溢出,10表示负溢出。最高符号位永远是真正的符号位,次高符号位参与运算,这个逻辑一旦用熟,比看进位判断快得多,也不容易漏。
很多同学会混淆“进位”和“溢出”。这两个概念必须分清楚:进位是最高有效位产生的进位输出,溢出是运算结果超出了机器字长能表示的范围。打个比方,体温计最高刻度是42度,你量出来43度,这不是“进位”的问题,是“溢出”了——超出了能表示的范围。进位的本质是量程内的位扩展,溢出是超量程,二者完全不是一回事。
2.3 原码/补码乘法与除法:从手算到机器实现的思维切换
乘法除法题是第三章里最劝退的部分。不少同学做原码一位乘法,手算算得飞起,但一让写递推公式就懵。问题出在把“手算过程”和“机器迭代过程”割裂了。
原码一位乘法的核心思想,说穿了就是:乘法可以拆成一系列加法和右移操作的循环。每次根据乘数的最低位决定加不加被乘数,然后部分积右移一位。注意这里是逻辑右移,因为原码乘法符号位单独处理,数值部分不带符号。每轮右移相当于把权重降一档,和十进制乘法的“错位相加”是一个道理,只是二进制里移位更简单。
补码一位乘法(Booth算法)看起来复杂,核心是引入了一个附加位,把乘数从低位往高位扫描,根据相邻两位的差值决定是加上被乘数、减去被乘数还是不动。这个算法最大的价值在于能统一处理负数,不需要像原码乘法那样把符号位剥离开。我自己做这类题的习惯是:先画出表哥,每一行记录“上次部分积+操作结果+右移后的部分积+乘数+附加位”,一步步往下填,闭着眼睛就不会漏步骤。
除法这块,原码恢复余数法和加减交替法(不恢复余数法)是重点。恢复余数法的逻辑简单但步数多,每次余数为负就恢复,相当于试商失败要回退。加减交替法则通过“余数左移一位,符号为正减除数,符号为负加除数”的规则避免了回退,效率高。这部分的习题一定不要只算答案,要跟住每一步的余数符号变化,因为后续的商位就是根据余数符号定的。
3. 精选课后习题详解
3.1 进制转换与表示范围类题目详解
习题1:设机器字长为8位,X = -29,求X的原码、反码、补码。
解析:这类题每次出现都有一堆人做错,原因不是不会转换,而是字长的限制没注意。
第一步,把29转成二进制。29 = 16 + 8 + 4 + 1 = 2^4 + 2^3 + 2^2 + 2^0,所以二进制为 1 1101,补齐8位数值部分就是 0001 1101。
第二步,加上符号位。X = -29是负数,符号位为1。所以原码 = 1001 1101。
第三步,求反码。反码是在原码的基础上,符号位不变,数值位按位取反。所以反码 = 1110 0010。
第四步,求补码。补码 = 反码 + 1 = 1110 0011。
验算一下:补码1110 0011,符号位为1说明是负数,数值部分取反加1得到 001 1101 = 29,加上负号得到-29,正确。
这类题我额外叮嘱一句:如果题目说“机器字长8位,求-29的补码用十六进制表示”,那就再走一步,把1110 0011从右往左四位一分,得到 E3。很多同学这里会栽在“符号位扩展”上,稍后第4章会有专门讲解,但建议现在就养成补码十六进制直接写对的习惯。
习题2:设机器字长为8位,用补码表示定点整数,求X = -128的补码。
解析:这道题是典型的“陷阱题”,陷阱不在计算,而在概念。8位补码的范围是-128到127,-128是刚好卡在边界上的值。
8位二进制补码中,正数范围是0000 0000到0111 1111(即0到127),负数范围从1000 0000开始。注意这里没有正0和负0之分,补码的0只有一个编码0000 0000,所以多出来的编码1000 0000就给了-128。
计算过程:128的二进制是1000 0000,但注意这是无符号视角。对于补码,-128直接记作1000 0000。你如果用“原码取反加1”的思路,会发现根本找不到-128的8位原码,这就是前面说的特例。
这类题的更一般形式是问“n位补码能表示的最小负数是多少”,答案直接写-2^(n-1)就行。
3.2 补码加减运算与溢出判断详解
习题3:设机器字长为8位,X = +100,Y = +50,用补码求X + Y,并判断是否溢出。
解析:第一步,转补码。X = 0110 0100,Y = 0011 0010。
第二步,直接相加:
0110 0100 + 0011 0010 ----------- 1001 0110结果的符号位是1,说明按补码解释这是一个负数。两个正数相加得到负数,这显然不合理——没错,这就是正溢出。+100加+50应该等于+150,但8位补码能表示的最大正数是+127,150超出了范围,所以溢出。
用双符号位验证:X变形补码为00 110 0100(两个符号位都是0),Y变形补码为00 011 0010,相加得到10 001 0110,两个符号位分别为1和0,不一致,判定溢出。最高符号位1是真正的符号,但次高符号位0说明进位没有传递到最高位——数据已经被截断了。这种“01”组合表示正溢出。
实际工程里怎么处理?溢出发生后,结果不可用,计算机会触发溢出异常或把结果丢弃,而不是“强行塞进8位里”。很多初学者会困惑:那+150用8位补码装不下,机器是不是就存了个错的?对,如果硬件不检测溢出,它就会按截断后的二进制解析出错误结果,正因为这样溢出检测电路(OF标志位)才如此重要。
习题4:设机器字长为8位,X = -64,Y = +32,用补码求X - Y,并判断是否溢出。
解析:这里的关键是X - Y = X + (-Y)。先把Y变成-Y。
Y = +32,补码为0010 0000。-Y就是-32,补码通过取反加1得到:1101 1111 + 1 = 1110 0000。
X的补码:-64 = 1100 0000。
相加:
1100 0000 + 1110 0000 ----------- 1010 0000最高位有进位1,但结果的符号位是1,按负数解释是-96。-64 + (-32) = -96,数学上正确。但这里有个更细致的点:单符号位判断法看的是“符号位进位”和“最高数值位进位”是否一致。最高数值位(次高位)产生了进位,符号位也产生了进位,两者相同,所以无溢出。
用双符号位验证:X = 110 0000补充成 11 100 0000,Y补码1110 0000补充成 11 110 0000,相加得 11 0110 0000,去掉最高的进位1,得到 11 0110 0000,两个符号位都是1,一致,无溢出。结果正确。
做这题容易出错的点是不知道X - Y要先把Y变号,很多同学拿-64直接减+32的补码,然后发现算出来的东西对不上号,就是因为漏掉了“减法转加法”时减数需要取补码这一步。
3.3 移位运算与舍入处理详解
习题5:设机器字长为8位,X = 0011 0110(无符号数54),分别逻辑左移一位、逻辑右移一位、算术右移一位,写出结果并说明数值变化。
解析:移位是个性价比极高的考点,规则不难,但需要分清类型。
逻辑左移一位:所有位左移,最低位补0。0011 0110左移一位变成0110 1100 = 108。54×2 = 108,正确。
逻辑右移一位:所有位右移,最高位补0。0011 0110右移一位变成0001 1011 = 27。54÷2 = 27,正确。
算术右移一位:算术移位针对有符号数,右移时最高位保持原符号位不变。这里X如果是补码表示的有符号数,符号位是0(正数),算术右移一位同样是0001 1011 = 27,因为正数的算术右移和逻辑右移效果一致。
为了区分,我通常补考一道负数移位题:设X = 1101 0010(这是-46的补码),算术右移一位结果是多少?最高位符号位1不动,其余位依次右移,最右边一位丢弃:1110 1001 = -23。-46÷2 = -23,符合预期。但逻辑右移则是0110 1001 = 105,数值含义完全不同。
移位运算在应用题里还有一个变形:用移位实现乘除2的幂。比如某题问“用移位和加法实现X×10”,参考答案是 X×10 = X×8 + X×2 = (X左移3位) + (X左移1位)。这种题在后面的指令系统章节还会反复出现,本质是放手算乘法、用机器擅长的移位和加法组合来算。
3.4 ALU功能表分析与加法器设计类题目详解
习题6:某8位ALU芯片的操作控制端为S0、S1、S2、S3,功能表部分内容如下。已知输入A = 1100 1100,B = 1010 1010,求当S3S2S1S0 = 1001(F = A + B)、S3S2S1S0 = 0110(F = A - B)时输出F的值和标志位。
| S3 | S2 | S1 | S0 | 功能说明 |
|---|---|---|---|---|
| 1 | 0 | 0 | 1 | F = A + B |
| 0 | 1 | 1 | 0 | F = A - B |
解析:ALU的题目说到底是“先算逻辑,再看标志位”。先看加法:A + B = 1100 1100 + 1010 1010 = 1 0111 0110,8位结果为0111 0110,最高位进位CF = 1。
注意这里的几个标志位:ZF(零标志)看结果是否为0,结果0111 0110非0,所以ZF = 0;SF(符号标志)看结果最高位,最高位为0,所以SF = 0;OF(溢出标志)看是否溢出。这里我们判断溢出要看的是“带符号数视角”:A的补码作为有符号数是-52(1100 1100的数值部分取反加1为0011 0100 = 52),B是-86,相加应该是-138,超出8位补码范围-128,明显溢出,所以OF = 1。但CF=1不一定是溢出,这一点和前面强调的一致。
再看减法:F = A - B = A + (-B)。-B就是把B按位取反加1:1010 1010取反是0101 0101,加1得到0101 0110。A加上这个值:1100 1100 + 0101 0110 = 1 0010 0010,8位结果为0010 0010 = 34。从有符号数角度验证:-52 - (-86) = 34,结果正确且未溢出,OF = 0。最高位进位CF = 1,这是减法中“借位取反”的体现,很多同学不理解为什么减法里CF=1反而代表没有借位,其实是因为补码减法统一成加法后,CF的含义变成了“无符号数加法是否产生进位”,需要单独记忆。
习题7:某加法器采用组间串行进位方式,由4个4位CLA加法器级联构成16位加法器。每个CLA加法器内部先行进位生成时间为T1,组间进位传递时间为T2,和输出时间为T3。求总加法时间表达式。
解析:这道题是热搜里“组间串行进位”词的常见出题形式,也是很多教材的课后原题。组间串行进位的含义是:每个4位CLA组内部是快速的先行进位,但组与组之间的进位是按串行方式传递的,即第1组的进位输出作为第2组的进位输入,依此类推。
总时间 = 第一组生成组进位的时间 + 后面3组每组传递进位的时间 + 最后求和的时间。第一组要等输入进位和A、B到达后,先经过内部逻辑生成组进位,时间记为T1;中间3组每组在获得输入进位后需要传递组进位,每组需要T2;最后一组的进位到达后,组内产生和输出,需要T3。总时间 = T1 + 3×T2 + T3。
如果扩展到n个组,总时间 = T1 + (n-1)×T2 + T3。这类题解题的唯一难点是搞清楚“第一组”和“最后一组”的特殊性,中间的组只做进位传递。很多人错在把T1乘了4次,这就是没理解串行进位的本质——只有第一组需要从零开始生成进位,后续组全部是等上游进位来了才能动。
4. 常见错误与排查技巧实录
4.1 符号扩展的坑:从8位到16位为什么会变号
课后题里经常出现“把8位补码扩展到16位”的题。比如将补码1000 1011扩展到16位。正确做法是把符号位1复制到高8位,得到1111 1111 1000 1011。不少同学直接把高8位补0,得到0000 0000 1000 1011,这就铸成大错——符号位变了,值从负数变成了正数。
这里我教一个百试不爽的判断方法:扩展前后的真值必须一致。1000 1011作为8位补码,是真值-117;扩展到16位后的补码也应该表示-117。你扩展完可以验证一下,值变了就是扩展错了。符号扩展不只在习题里出现,在汇编程序里从byte转word、在C语言里char转int,全是这个逻辑,这也是很多同学写程序出现隐式转换bug的根源。
4.2 溢出判断三种方法为什么有时候结果不一致
我在批改作业时发现,有些同学单独用三种方法判断同一道题,结果“不一致”,于是非常困惑。其实冲突的根源在于适用范围。
单符号位法(观察进位)和双符号位法适用于补码加减运算;而“正正得负、负负得正”的口诀只适用于两个同号数相加的场景,如果是异号数相加,永远不会溢出,但很多同学硬套口诀就会误判。还有一种情况,有些同学会用“结果的符号和加数符号不同就是溢出”来判断,这在同号相加时成立,但异号相减时也会出现“结果符号和减数相同”的情况,需要先化成加法再判断。
实操建议:考试时固定用双符号位法,因为它是从运算过程直接判断的,不容易受场景影响。所有加减法题目,先把两个数写成双符号位(正数为00开头,负数为11开头),相加后看结果的两个符号位。这个方法步骤多,但正确率最高,不需要绕弯子。
4.3 原码乘法中部分积的右移:到底是算术右移还是逻辑右移
原码一位乘法中,部分积始终是正数(因为符号位单独处理,数值部分都是绝对值),所以右移时最高位补0,这是逻辑右移。但补码一位乘法(Booth算法)中,部分积是带符号的,因此右移必须用算术右移,即最高位保持符号不变。两者的区别经常出现在填空题和应用题里,一个细节就是一分之差。
判断技巧很简单:看到“原码乘法”四个字,默认所有数值运算的都是绝对值,右移补0;看到“补码乘法”或者“Booth”,默认符号位参与运算,右移做算术移位。
4.4 除法中恢复余数法的“恢复”到底恢复的是谁
做题时还有个高频困惑:恢复余数法里,余数为负时,为什么要把余数恢复成上一步的正余数?
我举个直观例子:9÷4,第一步9减4得5,余数5为正,商上1;下一步5减4得1,余数为正,再上1;1减4得-3,余数为负,说明这一步减多了,要“恢复”回1,商上0。恢复的是“被错误减掉的余数”,即把减多了的那一步回退掉。这种试商逻辑在十进制除法里也有——你试商试大了,就要把商调小,把多减的部分加回来。
理解了这一点,你就明白为什么加减交替法效率更高——它不会退回去,而是根据余数符号直接决定下一步是“加除数”还是“减除数”,省掉了恢复操作,但每一步的余数可能是负数,需要额外记录。考试时如果时间紧张,优先用恢复余数法,因为它每一步都有明确的“恢复”锚点,不容易算飞。
5. 配套学习策略:从做题到真正理解运算器
5.1 用仿真实验验证理论运算
做第三章课后题,我强烈建议配合逻辑仿真工具LabVIEW或Logisim做一个4位加法器,把书上的“进位链”真正搭出来一次。
为什么建议搭硬件?因为课后题做的是“输入输出”层面的验证,一旦到了硬件层面,你会被迫理解进位怎么生成、怎么传递、哪些信号是并行产生的。比如组间串行进位和组内先行进位,看教材文字很容易糊涂,但在Logisim里点开芯片内部或观察引脚延迟,一眼就明白。
具体实验建议:先搭一个4位串行进位加法器,逐位观察进位传播;再搭一个4位先行进位加法器,观察组进位生成逻辑;最后用两个4位加法器搭一个8位组间串行进位加法器,对比三者的延迟差异。做完这三个实验,教科书上那些时序计算题就不再是背公式,而是变成你亲眼见过的事实。
5.2 期末与考研复习的侧重点差异
同样的课后题,期末复习和考研复习的用法完全不同。期末考试侧重“会算”,所以重点放在补码运算、溢出判断、原码一位乘法的手算画表题,这些题套路固定,练熟就行。考研则侧重“会推”,考生需要能推导Booth算法的递推公式、能从延迟角度分析加法器结构优劣、能结合后面的CPU设计章节综合出题。
如果你在准备考研,建议除了做题,还要学会给每个算法画“状态转移图”和“数据通路图”。考研大题经常给一个数据通路,问“乘数寄存器的位数是多少”“部分积寄存器初值是多少”“需要经历多少次加法和移位”,这些跨章节题目,本质上就是第三章的乘法算法加上第五章的数据通路分析。
5.3 资源推荐与刷题顺序
除了微课版教材的课后题,我建议按下面的顺序补充练习:先做王道考研的计算机组成原理章节练习,再做历年408真题中的相关题目,最后回到课后题二刷错题。
王道练习的特点是把考点按大纲压实,和教材章节的对应度很高;408真题的质量是最高的,运算器题目往往和前后的指令设计结合起来考,能训练你的综合能力。二刷课后题时,着重看那些第一遍做错的题,用一句话写下当时的错误原因,比如“忘了双符号位法”“减法忘记取补码”,考前只看这一句话清单,效率极高。
关于参考答案,目前网上流传的答案版本比较多,有些答案本身就有印刷错误。碰到看不明白的推导,优先对照教材例题的格式反推,比追着一个不明来源的答案硬看更有用。如果实在拿不准哪一步,可以按“先算数值、再验符号、最后看标志位”的顺序自己复核。
这一章是整个计算机组成原理里最“硬”的地方,后面讲到CPU、指令系统、存储系统,很多东西都得回来找运算器的基础。课后题做错不可怕,可怕的是做错了不看过程只想抄个正确答案。每次一看到“补码”“溢出”这些词就条件反射地想起那双符号位的判断规则,你这一章就算真正学踏实了。我自己当年也是在第三章翻了车,后来痛定思痛把每一类题的推导过程都白纸黑字写了一遍,才在后来的系统结构课程里游刃有余。希望这份解析能帮你少走这段弯路。