news 2026/10/1 11:28:20

补码乘法详解:Booth一位乘法原理、手算与C语言实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
补码乘法详解:Booth一位乘法原理、手算与C语言实现

说实话,补码的乘运算这门课,刚学时很多人都会被“符号位到底参不参与运算”“最后移位又移的是什么”这些细节绕晕。刷了无数遍王道视频、啃了唐朔飞的教材,最后发现核心其实就一句话:补码乘法不用单独处理符号位,符号位直接参与运算,而实现这个“直接参与”的算法,就是Booth一位乘法。这篇文章我不讲虚的,直接把这套算法的来龙去脉、手算流程、C语言模拟,以及我当年踩过的一堆坑全部端出来。

1. 先理清基础:补码、双符号位和算术右移

1.1 补码的编码逻辑:为什么二进制世界爱用补码

我们学原码、反码、补码的时候,总觉得补码就是个“取反加一”的操作。但补码真正的价值在于:它把减法统一成了加法。有了补码,计算机里就不需要专门的减法器了。这是硬件设计上极其重要的简化。

补码的定义可以用“模”来理解。对定点整数来说,n+1位补码(含符号位),它的含义是:

[x]补 = 2^(n+1) + x (mod 2^(n+1))

对定点小数来说:

[x]补 = 2 + x (mod 2)

这个“模”的概念一到位,很多问题就通了。比如为什么负数补码取反加一?因为要满足 [x]补 + [-x]补 ≡ 0 (mod 2^(n+1))。加法器里一旦产生最高位的进位,就直接丢弃,这也是补码运算“模”性质的体现。

补码还有一个很重要的性质,就是真值 x 和补码的关系可以写成:

x = -x0 + Σ(i=1 to n) xi * 2^(-i)

其中 x0 是符号位。这个公式在Booth算法的推导中会直接用到,先记住它。

1.2 双符号位到底是干嘛的

很多同学看到补码乘法里被乘数要用“双符号位”(也叫变形补码),就有点懵:符号位一个不够,还要两个?

双符号位的本质是给溢出判断留了空间。正常情况下,两个符号位应该相同:正数两个0,负数两个1。如果运算过程中两个符号位变成了01或10,那说明结果溢出了。注意,补码乘法的过程中部分积是会溢出的,如果没有第二个符号位兜底,一次进位就可能把符号位冲掉,导致后续计算全错。

举个例子,两个负数相加,符号位都是1,正常两个1相加会有进位。如果只有一个符号位,这个进位直接覆盖了符号位本身,得到的结果符号就反了。双符号位可以多接住一次进位,让结果保持正确。补码乘法过程中“先加后移”的操作模式,中间结果很容易出现需要双符号位才能正确表示的情况。

1.3 算术右移和逻辑右移的区别,千万别搞混

移位是补码乘法的核心操作,但很多同学手算时最爱在这里翻车。

右移分两种。逻辑右移就简单粗暴:高位一律补0。而对补码来说,逻辑右移会改变负数的真值,因为补码的负数是高位一连串1,你在左边补0,这个数的数值意义就变了。

算术右移不一样,它右移时左边补的是符号位本身。正数高位补0,负数高位补1,这样每右移一位就相当于真值除以2。补码乘法里的部分积右移,必须用算术右移。乘数寄存器Q在联合移位时,最高位接收的是ACC的最低位移过来的数据,而不是保持自己的符号位,这个细节后文再细讲。

2. 深入Booth一位乘法:从原码乘法的痛点说起

2.1 原码乘法的麻烦在哪

原码一位乘法大家应该都知道:符号位单独异或处理,数值位绝对值相乘,每步根据乘数当前最低位是1还是0决定加不减被乘数绝对值,然后右移一位。

最大的问题有两个。第一,符号位单独处理,那硬件上要么多一个符号判断逻辑,要么额外多一次异或。第二,乘数的符号位在移位过程中是特殊处理的,不能完全当作普通位来移。这在硬件实现上就得多出控制逻辑,不够统一。

补码乘法如果能做到符号位和数值位“一视同仁”,硬件实现就简洁多了。Booth一位乘法就是干这件事的。

2.2 Booth算法的核心推导:从相邻位的差值出发

我当初学Booth算法的时候,最不理解的就是:为什么每次要盯着乘数的最低两位,而且是Q0和附加位Q_(n+1)这一对“相邻位”来判决。

这里的逻辑推导很漂亮。设乘数 y 的补码是 y0.y1 y2 ... yn,根据1.1节那个补码真值公式:

y = -y0 + y12^(-1) + y22^(-2) + ... + yn*2^(-n)

现在我们来做一个小把戏,把这个式子改写一下。引入一个 y_(n+1) = 0(这就是附加位的来历),可以把 y 改写成:

y = (y1 - y0)*2^0 + (y2 - y1)*2^(-1) + (y3 - y2)*2^(-2) + ... + (0 - yn)*2^(-n)

你展开一下就能验证:y1的系数是 1 - 1/2 = 1/2,y2的系数是 1/2 - 1/4 = 1/4,以此类推,最后yn的系数还是2^(-n),而 -y0 项完整保留。所以这个展开式和原式完全等价。

最关键的是每个括号 (y_(i+1) - y_i) 只会是 -1、0、1 三种值。括号等于0就什么都不加;括号等于1就加被乘数;括号等于-1就减被乘数(也就是加被乘数的相反数,即加[-x]补)。判决依据就看当前相邻两位y_i y_(i+1):

  • 00 或 11:差值0,加0
  • 01:差值1,加[x]补
  • 10:差值-1,加[-x]补

这就是Booth算法那张判决表的来历,完完全全从数学上推导出来的,不是拍脑袋定的规则。

2.3 寄存器布局:ACC、MQ、附加位

硬件或模拟过程中,我们需要三样东西:

  • ACC(累加器):存放部分积,采用双符号位加n位数值位的格式。初始为0
  • MQ(乘商寄存器):存乘数y的补码,单符号位加n位数值位。初始就是[y]补
  • 附加位C:在MQ的最低位后面额外加一位,初始值固定为0

这里要特别注意位宽。假设数值位有n位,那么ACC是n+2位(双符号位+ n位数值),MQ是n+1位(1符号位 + n位数值),再加1位附加位。手算时经常写成:

ACC = 00.0000(这里假设n=4,双符号位+4位数) MQ = 1.0101(y的补码,单符号位+4位数) 附加位 = 0

2.4 算法流程:几轮判决,几次移位

补码一位乘法(Booth法)的完整流程:

  1. 初始化ACC=0,MQ=[y]补,附加位=0
  2. 重复以下过程n+1次:
    • 检查MQ最低位Q0和附加位C的取值组合
    • 按组合表对ACC加对应值(0、[x]补或[-x]补)
    • 若还未到第n+1次,则ACC、MQ、附加位联合右移一位(算术右移)
  3. 最后一次(第n+1次)只做判决加法,不再右移

注意我这里特别写了“n+1次判决、n次移位”。这是很多资料里最容易让新手混乱的细节。有的教材写成“n次加法和n次右移”,那种写法通常默认最后一次加法已经含在初始状态里。咱们按n+1次判决来算,思路是干净的。为什么最后一次不右移?因为前面的n次右移已经把部分积放到了正确的位置,最后一步加完就是最终结果,如果再右移就等于整体多除了一个2,结果就错了。

3. 完整手算:x=0.1101,y=-0.1011

3.1 准备数据,确定码制

来看一个最经典的考研级例题。设:

x = +0.1101,y = -0.1011

先把所有码制都写清楚:

  • [x]补 = 00.1101(双符号位)
  • [-x]补 = 11.0011(这是 x 相反数的补码,也就是对[x]补整体再取一次相反数)
  • [y]补 = 1.0101(单符号位,MQ初始值)
  • 附加位初始 = 0

数值位n=4,所以整体流程是5次判决、4次右移。

这里有个容易出错的地方:[-x]补是怎么来的?是对 00.1101 连同符号位一起取反加一,得到 11.0010 + 1 = 11.0011。很多同学手算时只对数值位取反,符号位不动,那就错了。补码的求相反数操作,符号位是连坐的。

3.2 五位判决、四次移位的完整过程

我直接给一张我当年复习时自己整理的过程表,每一行对应一次判决加移位后的状态。

步骤判决组合(Q0 C)操作ACCMQ附加位C
初始--00.00001.01010
110(Q0=1,C=0)ACC+[-x]补,右移11.0011 → 右移后 11.10011.10101
201(Q0=0,C=1)ACC+[x]补,右移11.1001+00.1101=00.0110 → 右移后 00.00110.11010
310(Q0=1,C=0)ACC+[-x]补,右移00.0011+11.0011=11.0110 → 右移后 11.10110.01101
401(Q0=0,C=1)ACC+[x]补,右移11.1011+00.1101=00.1000 → 右移后 00.01000.00010
510(Q0=1,C=0)ACC+[-x]补,不移位00.0100+11.0011=11.01110.00010

强调一下,表里的MQ我写的是移完之后的完整值,看起来“符号位”变了,但那已经不是乘数符号了,它只是ACC最低位移过来之后的数据,别再用乘数符号的视角看它。

3.3 结果拼接与验证

第5步结束后,ACC=11.0111,MQ=0.0001。最终结果怎么拼?

乘积总位数是2n+2位,即双符号位2位+8位数值位。取ACC完整的6位(11.0111),再接上MQ的低4位(因为MQ高1位是移位过程中从ACC挤过来的,不是有效数值位)。所以:

乘积 = 11.0111 + 0001 = 11.01110001

来验证一下。真值计算:0.1101 = 13/16,-0.1011 = -11/16,乘积 = -143/256。

看补码 11.01110001,这是负数。数值位 01110001,取反加一:10001110+1=10001111 = 143。确实是 -143/256。完全吻合。

所以整个手算过程验证下来,Booth算法每一步都自洽。建议你自己独立算一遍,尤其注意第2步和第4步加完之后的ACC进位情况,感受一下双符号位是怎么“吞掉”进位的。

4. C语言模拟Booth乘法:把算法跑起来

手算懂了之后,我建议你写个几十行的C语言程序把它模拟一遍。写代码的过程能把很多模糊的细节逼出来,比如位宽截断、右移方向、附加位更新时机。下面是我当时的一个精简版本,核心逻辑都在。

#include <stdio.h> // 用整数的低6位表示ACC(双符号位+4位数值) // 用整数的低5位表示MQ(单符号位+4位数值) // 为了清晰,这里每一步都打印状态 int main() { // 准备数据:x=+0.1101, y=-0.1011 int x = 0b001101; // [x]补 = 00.1101 int neg_x = 0b110011; // [-x]补 = 11.0011 int mq = 0b10101; // [y]补 = 1.0101 int acc = 0; // 部分积初始为0 int c = 0; // 附加位Q(n+1),初始0 int mask_acc = 0x3F; // 保留6位 int mask_mq = 0x1F; // 保留5位 printf("初始: ACC=%06b MQ=%05b C=%d\n", acc, mq, c); for (int step = 1; step <= 5; step++) { // n+1 = 5次判决 int q0 = mq & 1; int combo = (q0 << 1) | c; // 高1位是Q0,低1位是C if (combo == 0b01) { acc = (acc + x) & mask_acc; // 01:加[x]补 printf("步骤%d: 组合01,ACC+[x]补 -> %06b\n", step, acc); } else if (combo == 0b10) { acc = (acc + neg_x) & mask_acc; // 10:加[-x]补 printf("步骤%d: 组合10,ACC+[-x]补 -> %06b\n", step, acc); } else { printf("步骤%d: 组合00或11,ACC+0 -> %06b\n", step, acc); } if (step < 5) { // 前4次右移,最后一次不移 int sign = (acc >> 5) & 1; // 取ACC符号位 // 把ACC(6位)、MQ(5位)、C(1位)拼成一个12位数,再整体右移 int total = (acc << 6) | (mq << 1) | c; total = (total >> 1) | (sign << 11); // 算术右移,高位补符号位 total &= 0xFFF; acc = (total >> 6) & mask_acc; mq = (total >> 1) & mask_mq; c = total & 1; printf(" 右移后: ACC=%06b MQ=%05b C=%d\n", acc, mq, c); } } printf("最终: ACC=%06b MQ=%05b C=%d\n", acc, mq, c); return 0; }

这段代码的思路就是严格按照表里的流程来走。关键点在于“联合右移”那一步:把ACC、MQ、附加位三个寄存器拼成一个连续的位串,整体右移一位,左端补的是ACC的符号位(sign),而不是补0。这就是算术右移在寄存器级别的实现。

运行结果你会看到每一步的状态变化,和第三章那个手算表一一对应。我当初调这个代码时就发现,如果右移时忘了补符号位,最后一位的结果必然对不上,这本身就是理解算术右移最好的实验。

5. 常见错误与排查技巧实录

5.1 附加位初始值的坑

附加位初始必须是0。很多人会问,为什么乘数补码后面还要人为加一个0?回到2.2节的推导,我们是引入 y_(n+1)=0 来做相邻位差的,所以这个初始0是推导过程的一部分,不是随便加的。但如果乘数是负数补码,比如1.0101,把它当成一个整体,附加位还是0,不要因为符号位是1就把附加位也置1。

5.2 移位时补0还是补符号位,一定要分清

部分积ACC右移时必须补符号位,也就是算术右移。很多同学在这里用逻辑右移,给负数部分积补了个0,结果每移一次,数值就错一次。最简单的方法就是用我代码里的思路:先取ACC最高位,然后整体右移时把这一位补到最前面。补码的算术右移等价于真值除以2,这是整个算法的数学基础,破坏了这一点,后续全部错。

还有一个易错点:MQ在联合右移时,最高位接收的是ACC移出来的最低位,不是保持MQ自身的符号位。MQ的符号位在第一次移位后就没有意义了,它只是乘积低位的临时存放处。手算时不要看到MQ里符号位变了就觉得是错。

5.3 最后一次到底要不要移位

答案是:最后一次判决后不右移。原因前面说过,Booth法的迭代公式决定了第n+1次加法之后恰好是最终结果。如果你多右移一次,结果等于整体除以2,符号位也可能被破坏。考试时算到最后一位,一定要停下来,别顺手又移一次。

5.4 结果拼接时MQ的位宽问题

这是最隐蔽的一个坑。我曾见过不少同学算完ACC=11.0111、MQ=00001后,直接把两者拼成11.011100001,多了一位,结果怎么验都不对。

正确做法是:最终乘积取ACC完整6位 + MQ的低4位(也就是去掉MQ的最高位)。因为MQ总共5位,最高位在移位过程中接收的是ACC的最低位移过来的值,它实际是乘积的中间扩展位,不构成最终乘积的有效数值。拼接后应该是11.01110001而不是11.011100001。多出来的那一位会让结果凭空多一个2的负幂次,误差就出来了。

5.5 双符号位溢出判断

运算过程中,如果ACC两个符号位不一致,比如变成了01或者10,说明这一步加法溢出了。但在Booth算法里,这种情况大多不是真正的溢出,而只是中间结果超出了单符号位的表示范围,双符号位正好兜住了。操作上不用特殊处理,继续移位就行,符号位会自动调整回来。真正需要警惕的是最终结果的两个符号位不一致,那说明乘积本身超出可表示范围,这时候才叫真溢出。

5.6 考研、期末常考题型速查

题型考察点易错点
给x、y补码,手算Booth乘法判决表记忆、移位次数最后一次移位、MQ位宽
判断补码乘法溢出双符号位判决把中间状态误判为溢出
补码右移一位求值算术右移规则负数补0的错误
求[-x]补符号位一起取反加一只对数值位取反
结果真值转换补码转真值忘了符号位影响

5.7 我的独家记忆技巧

最后分享几个我当年死磕出来的小技巧。

第一,判决表别死记。你就想“01”是从0变1,相当于乘数在这一位上从无到有,所以要加被乘数[x]补;“10”是从1变0,相当于从有到无,所以要减被乘数,加[-x]补。00和11就是没变化,加0。这样记,比背表稳得多。

第二,移位次数记“n次移位,n+1次判决”。数值位是4位就右移4次、判断5次,数值位是8位就右移8次、判断9次。这是通用规律。多出来的一次就是不右移的收尾加法。

第三,手算时把ACC、MQ、附加位三个东西竖着排,每做完一步就更新一整行,用箭头标出“右移”前后状态。这样最后出了错也能顺着表往回查,比闷头算到底再回头找错快得多。

Booth一位乘法只是补码乘法入门,后面还有补码两位乘法(Booth两位乘)、阵列乘法器、流水线乘法器等等,但核心的判决思维和移位思想是互通的。把这张表吃透、把例子亲手算三遍、把代码跑通,补码乘法这一块就稳了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 11:28:17

本科生毕设可用的轻量超分模型RLFN实战指南

简介&#xff1a;本资源是一份面向本科毕业生与深度学习初学者的图像超分辨率毕设完整实践包&#xff0c;聚焦于基于Python实现的轻量级高效网络RLFN&#xff08;残差局部特征网络&#xff09;研究。项目涵盖理论基础、模型设计、训练验证与结果分析全流程&#xff0c;特别适合…

作者头像 李华
网站建设 2026/10/1 11:27:52

Allegro 16.6 效率配置指南:从快捷键到Gerber导出的实用避坑手册

年前换工作电脑&#xff0c;把常用的 Cadence Allegro 16.6 重新装了一遍&#xff0c;又老老实实把那些散落在各处的小设置一个个找了回来。说实话&#xff0c;Allegro 这个软件功能强大是强大&#xff0c;但很多好用的配置藏得深&#xff0c;不常折腾的人根本不会去翻。我当年…

作者头像 李华
网站建设 2026/10/1 11:27:30

梯级水光互补系统短期优化调度:基于Python的期望值建模与实现

这段时间一直在啃一篇EI期刊上的梯级水光互补系统短期优化调度论文&#xff0c;核心思路其实一句话就能讲明白&#xff1a;光伏出力存在不确定性&#xff0c;模型要在这种随机波动环境下&#xff0c;合理安排梯级水电站各时段的发电流量与弃水策略&#xff0c;使“水电光伏”整…

作者头像 李华
网站建设 2026/10/1 11:27:30

eNSP企业网三层拓扑实战:可运行、可验证、可排错

简介&#xff1a;本资源是一套基于华为eNSP平台构建的企业级网络模拟实验环境&#xff0c;面向网络工程初学者、高校通信/计算机专业学生及备考HCIA/HCIP认证的工程师&#xff0c;解决网络规划设计与安全策略落地缺乏实操场景的问题。压缩包共32个文件&#xff0c;含12个efz设备…

作者头像 李华
网站建设 2026/10/1 11:27:28

PSO-BP回归预测:小样本高噪声工业数据建模实战

简介&#xff1a;本资源是一套基于Python实现的PSO-BP混合回归预测模型代码包&#xff0c;面向机器学习初学者与数据挖掘实践者&#xff0c;解决BP神经网络易陷局部最优、参数初始化敏感等典型问题。通过粒子群优化算法自动搜索最优权重与偏置&#xff0c;显著提升非线性回归任…

作者头像 李华
网站建设 2026/10/1 11:27:26

结构半群与分形时间框架:时序建模的范畴化基础与多表示等价性

前两天被同事甩了个标题过来&#xff0c;叫“结构半群与统一表示&#xff1a;历史依赖自适应分形时间框架的范畴化基础与多表示等价性”。第一眼确实有点劝退&#xff0c;满屏幕都是学术黑话。但等我把它拆开&#xff0c;对应到实际做过的时序建模、信号分析和状态推演项目里&a…

作者头像 李华