补码乘法右移补0还是补1?一文讲透算术右移与符号扩展
2026/9/16 6:16:12 网站建设 项目流程

不要小看“补码两位乘法右移时空出来的高位到底补0还是补1”这个问题。它表面上只是一个小判断,实际上牵扯到补码的符号扩展、算术右移和寄存器拼接这三条线。我见过不少基础不错的同学,Booth算法步骤背得滚瓜烂熟,一落到寄存器层面就翻车:有的在部分积为负数时右移补了0,有的把乘数右移时的高位补位当成“补0或补1”来处理,最后乘积符号都对不上。原因很简单——老师讲乘法时用一个符号位,讲移位时又默认算术右移,课下做题时脑子里装的却是逻辑右移的“空位补0”,两套规则一打架,结果自然错。

这篇文章就围绕补码一位乘法和补码两位乘法展开,重点解决“移位时补0还是补1”这个判断。不管是考研、期末考试还是面试手撕补码乘法,搞清楚下面这几件事,你就能把这种题做对,还能顺带解释清楚“为什么”要这样补。

1. 先厘清两种右移:逻辑右移和算术右移,别让“补0”的习惯坑了你

1.1 为什么会有“空出来的位”需要补

寄存器是固定位宽的,比如8位寄存器存了一个数,右移一位后,最左边那一位就空出来了。空出来的这一位不能悬空,硬件上必须给它填一个确定的值。问题就在这个“填什么”上。

对于无符号数,右移后高位补0,这个大家都没争议。但对于有符号数,高位补0还是补1,直接决定了这个数在右移之后还是不是原来的数值。很多人第一次学的时候,被“右移就是除以2”这句话洗脑,看到所有右移都认为补0,结果处理负数时彻底翻车。

举个例子:4位补码1100表示的是 -4。如果右移一位后高位补0,得到0110,也就是 +6;如果高位补1,得到1110,也就是 -2。哪个才是 -4 除以 2 的结果?显然是 -2。所以对于补码表示的有符号数,右移时必须补符号位,不能见空就补0。

1.2 逻辑右移和算术右移的区别

两种右移的定义很简单:

右移类型面向对象高位补什么例子:4位1010右移1位
逻辑右移无符号数补00101
算术右移有符号补码数补原符号位1101

逻辑右移纯粹是把二进制串往右推,空位补0,相当于无符号整数除以2后向下取整。算术右移则要保持补码数的符号不变,所以最高位原来是0就一直补0,原来是1就一直补1,这也叫“符号扩展”。

判断方法也很简单:右移之前看一眼最高位,也就是符号位。符号位是0,右移补0;符号位是1,右移补1。右移几位,就连续重复填几个符号位。

1.3 补码乘法里用的是算术右移,不是逻辑右移

补码乘法里,部分积寄存器、乘数寄存器保存的都是补码数,中间结果也全都是带符号数。整个乘法过程中只要发生右移,就必须保证“除以2”的语义正确。换句话说,乘法里所有右移都是算术右移。

这就是回答标题那个问题的总原则:

补码乘法移位时,高位补0还是补1,取决于被移位寄存器原来的最高位(符号位),也就是最高位是0就补0,最高位是1就补1。

这个概念清楚之后,下面看补码一位乘法和两位乘法里具体怎么用。

2. 从补码一位乘法看答案:符号位才是真正的“补位来源”

2.1 Booth一位乘法的基本流程

补码一位乘法最常用的是Booth算法。它的核心思想是:不一定每一位都要加被乘数,通过观察乘数当前最低位和附加位的关系,决定这一步是加、减还是不动,这样符号位也能统一处理。

规则是:在乘数最低位后面额外补一个附加位,记为Q-1,初始为0。每一步看“乘数当前最低位Q0”和“附加位Q-1”的组合:

Q0与Q-1组合操作
00部分积加0,右移一位
01部分积加X,右移一位
10部分积减X(加-X的补码),右移一位
11部分积加0,右移一位

每一步执行完加法后,都要把“部分积A + 乘数Q + 附加位Q-1”整体算术右移一位。这里的核心就是:A寄存器的高位补什么,看A寄存器当前的最高位(符号位)。

2.2 部分积为负时,右移必须补1

我直接给一个手算过程中的片段。假设当前部分积A是11.0011,这是 -0.8125 的补码(双符号位表示)。这时候要做一次右移一位。

算术右移时,先把最高位符号位复制一份放到新空出来的最高位,然后原来的每一位依次往右挪。11.0011右移一位,得到11.1001,最高位还是1,高位补的也是1。

从真值上看也说得通:-0.8125 除以2等于 -0.40625,而11.1001正是 -0.40625 的补码。如果这时候脑子一抽补0,变成01.1001,那就是一个正数了,整个乘法结果直接报废。

这就是“补1”出现的场景。不要觉得补1奇怪,只要部分积是负数,右移就必须补1。补1不是错误,是补码算术右移的正常动作。

2.3 部分积为正时,右移补0

再看另一个片段。假设当前部分积A是00.1100,这是 +0.75 的补码。右移一位,最高位是0,所以高位补0,得到00.0110,也就是 +0.375。0.75除以2正好是0.375,完全对得上。

所以正数补0、负数补1,本质上是同一个规则:看部分积最高位的符号位。符号位是0,补0;符号位是1,补1。

2.4 阶段结论:先看符号位,再决定补什么

到这里可以先停下来总结一句:

补码一位乘法中,每步右移都是算术右移。右移前盯着部分积A的最高位,它是0,右移空位补0;它是1,右移空位补1。与乘数Q是0还是1无关,与加的是X还是-X也无关。

很多同学喜欢背“负数补1正数补0”,这句话本身没错,但最好理解成“补的是符号位本身”,而不是额外记忆一个特例。

3. 补码两位乘法:查表操作更多,高位补位规则升级为重复符号位

3.1 两位乘法为什么快:一次判断处理乘数两位

补码两位乘法的本质,是把Booth一位乘法里相邻两步合并成一步。一位乘法每步只看乘数1位和附加位,决定加什么;两位乘法每步看乘数连续两位和附加位,决定加的是0、X、2X、-X还是-2X,然后一次性右移两位。

这样迭代次数直接减半。比如乘数有4位数值位,一位乘法要做4次加法和4次右移,两位乘法只需要做2次加法和2次右移,所以工程上速度更快。

常用操作判断表如下(不同教材写三位编码的顺序可能不同,但操作含义一致,考试时以你手上的课件为准):

三位编码对应操作
000部分积加0
001部分积加X
010部分积加X
011部分积加2X
100部分积减2X
101部分积减X
110部分积减X
111部分积加0

每完成一次加/减,就要把“部分积A + 乘数Q + 附加位Q-1”整体算术右移两位。注意是右移两位,不是一位。

3.2 为什么两位乘法必须用双符号位

一位乘法里,部分积加的是X或-X,部分积范围还比较小;两位乘法里会出现加2X或减2X,中间结果的绝对值可能超过1。如果部分积寄存器只有一位符号位,遇到超过1的数就没法表示了。

所以通常会用双符号位,也就是部分积A写成00.xxxx11.xxxx,多出来的一个符号位用来容纳临时结果。

举个例子:X = 0.1101,2X = 1.1010。这个值超过了1,如果用双符号位表示,就是01.1010。这里的“01”并不是错误,它表示一个正的临时溢出,在乘法过程中是允许出现的。只要后面右移回去,结果又能回到正常范围内。

但也因为双符号位的存在,很多同学在“补0还是补1”这里又乱了一次:看到01.xxxx的时候,以为最高位是0就补0;看到10.xxxx的时候,以为最高位是1就补1。其实这个理解也正确,但必须说得更精确:看的是双符号位里的最高位,也就是最左边那一位。

3.3 右移两位时的补位规则

双符号位情况下,右移两位时要把符号位连续填两次:

右移前A的最高两位右移两位后最高两位补什么
00补00
11补11
01补00(最高位是0)
10补11(最高位是1)

这里最容易错的是0110这两种临时溢出状态。很多同学看到01里的次高位是1,就误以为这是负数,右移补了两个1;看到10里的次高位是0,就以为补两个0。正确做法是只看最左边那一位,也就是真正的符号位。

举几个具体例子:

  • 00.1101右移两位 →00.0011(高两位补00)
  • 11.1001右移两位 →11.1110(高两位补11)
  • 01.1010右移两位 →00.0110(虽然双符号位是01,但最高位是0,补00)
  • 10.0110右移两位 →11.1001(最高位是1,补11)

你看,即使是临时溢出状态,只要坚持“按最高位补符号位”的原则,所有情况都能统一处理。

3.4 两位乘法里“补1”出现得更频繁

两位乘法的操作码里出现了-2X、-X,减完之后的中间结果很容易变成负数。所以做两位乘法时,右移遇到补1的频率比一位乘法高得多。这不是偶然,而是因为部分积经常在正负之间摆动。

如果你做两位乘法时从头到尾只看到补0,那大概率哪里算错了,或者你一直在做正数乘正数的特殊情况。

4. 做题最容易踩的坑:附加位、最后一移、乘数右移

4.1 附加位初值必须是0,右移后要承接乘数移出的位

补码一位乘法和两位乘法都在乘数最低位右侧引入了一个附加位Q-1,初值固定为0。每步右移后,附加位要更新成乘数原来最低位移出来的那一位。

很多同学手算时,只盯着部分积A怎么补位,却忘了Q-1的更新。Q-1一旦写错,下一步的加/减判断就会跟着错,结果一路崩到尾。

注意:附加位不是乘数的有效位,它只是判断加/减时用的“历史位”。考试时把Q-1单独列一列,和Q寄存器分开写,能减少犯错。

4.2 最后一次加/减后不要再右移

补码一位乘法中,乘数如果包含符号位一共n+1位,那么要做n+1次加/减判断,但只做n次右移。也就是说,最后一次判断完成后,只在部分积上做加或减,不再右移。

两位乘法也有类似情况:当乘数位数不够凑成偶数位时,要先在符号位后补0凑成偶数位;最后一步也要根据具体情况决定是否需要右移。如果考试时多移了一次,结果的小数点位置就会偏一位。

这个坑特别隐蔽,因为很多教材的表格式步骤里,最后一步看起来和前面一样,容易让人习惯性右移。建议每次做完加/减后先问自己:这是最后一次吗?是,就别右移。

4.3 乘数右移补的是A的最低位,不是补0也不是补符号位

这个问题问的人特别多:Q寄存器右移后,高位空出来的位置补0还是补1?

正确答案是:都不对。补码乘法移位时,并不是各寄存器单独右移,而是把A、Q、Q-1拼在一起整体算术右移。整体右移时,A的最低位会移入Q的最高位,Q的最低位移入Q-1,只有A的最高位才按符号位补。

所以你不能单独问“Q右移高位补什么”,而应该说“Q的高位接收的是A的最低位”。这也是我建议大家在草稿纸上把A和Q紧挨着写、不要留空格的原因。你一旦把A和Q分开写,就会忍不住把Q当成独立寄存器,然后开始纠结补0还是补1。

4.4 双符号位出现01或10时,先看最高位,再判断要不要停

两位乘法中间出现01或10是正常现象,尤其是加2X之后。这时候不要急着判定“溢出出错”。补码两位乘法允许这种临时状态存在,只要在下一步右移时正确扩符号位,最终结果还是对的。

但必须留意:如果在最后一次加/减之后,部分积仍然出现01或10,那才是真的溢出,说明结果超出了寄存器能表示的范围。考试中一般不会出这种极端题,但你得知道这个区分。

5. 考场速判与几个真实心得

5.1 三句口诀

我在辅导学生时总结了三句话,基本能覆盖补码乘法右移的所有情况:

右移先看最高位,符号0就补0,符号1就补1。 移两位就重复填,00填00,11填11。 乘数缺位找A补,A的最低位进Q的最高位。

第一句管部分积A,第二句管两位乘法的双符号位,第三句管Q寄存器的补位。三句话连起来,就是一个完整的算术右移过程。

5.2 手写建议:每步列三列

考试手算时,建议每步都列成三列:

  • 第一列:当前的加/减操作,比如“加-X”或“加2X”;
  • 第二列:更新后的A值;
  • 第三列:更新后的Q和Q-1值。

每次右移之前,先把A的最高位圈出来。比如A是11.1001,就把最左边的1圈起来,告诉自己“这一步右移要补1”。这是个笨办法,但真的能防止手滑。

5.3 学有余力:写个几行脚本验证手算

如果你在做题时对自己的手算结果拿不准,可以用一个简单的模拟脚本验证。下面这个例子是用Python模拟补码一位乘法的核心循环,方便对照步骤:

def arith_shift_reg(a_bits, q_bits, q1_bit): # 整体算术右移一位,a_bits和q_bits是字符串表示的补码位 sign = a_bits[0] q_out = q_bits[-1] a_bits = sign + a_bits[:-1] q_bits = a_bits[-1] + q_bits[:-1] return a_bits, q_bits, q_out # 使用示例:初始A=00.0000, Q=10101, Q-1=0 a = "000000" q = "10101" q1 = 0 print(a, q, q1)

这个脚本只是演示寄存器的移位逻辑,真正的Booth算法还要根据Q最低位和Q-1的值决定加还是减。但只要你理解了“整体算术右移”,把加/减逻辑补进去不难。我建议手算一两道题之后再写这个脚本,因为你只有亲手算过,才知道哪些步骤容易错。

5.4 一点个人体会

我本科刚学补码乘法时,也背过“补0还是补1”的结论,结果一换题目就错。后来我换了一个思路:把每一步右移都翻译成“这个数除以2”,然后问自己,这个结果还是不是原来的数除以2。从真值角度反推,永远比死记结论可靠。

补码两位乘法也是一样。不管它一次查三位、加2X、减2X,右移的本质始终是算术右移。你只要看到被移位的寄存器里保存的是补码数,就记住一句话:高位补的是符号位,不是0,也不是1,而是“原来的最高位”。想清楚这一层,补码一位乘、两位乘、甚至以后遇到硬件乘法器里的移位逻辑,你都不会再被这个问题卡住。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询