新闻详情

新闻详情

首页 / 资讯中心 / 详情

二进制补码有符号数乘法:从符号扩展到Booth算法的完整指南

发布时间:2026/10/2 19:58:44来源:尧图网络
二进制补码有符号数乘法:从符号扩展到Booth算法的完整指南
先说个真事。去年帮一个做数据采集的朋友排查问题他拿MATLAB解析一串十六进制报文里面有温度、有电压结果所有负温度全变成了三百多度。代码翻来覆去看了半天最后定位到一句hex2dec之后直接参与运算一个0xFE被当成 254 处理后面的加减乘除全跟着错了。这种问题根子不在MATLAB在于对“有符号数在机器里到底长什么样、乘法又是怎么算的”没建立起直觉。这篇东西我想把二进制补码下的有符号数乘法一次说透。不管你是写C、写Verilog还是拿MATLAB做数据解析只要你跟底层数据打过交道迟早会撞上补码乘法这个坎。我会从补码的数学本质讲起把符号扩展法、Booth算法、代码实现和常见坑全部串一遍配合完整的手算例子争取让你看完之后再遇到负数乘法不再“懵”。1. 从一次线上事故说起补码乘法为什么容易翻车1.1 补码的本质一个公式看懂补码很多人学补码的时候背的是“取反加一”。这个口诀没错但只告诉你“怎么变”没告诉你“为什么”。真正要理解有符号数乘法必须回到补码的数学定义。对n位二进制数 (x x_{n-1}x_{n-2}...x_1x_0)它作为补码表示的真值是[ x -x_{n-1} \cdot 2^{n-1} \sum_{i0}^{n-2} x_i \cdot 2^i ]翻译成人话最高位是符号位但它不只是“符号”它带的是负权值。一个4位补码1101按这个公式算就是 (-1 \times 8 1 \times 4 0 \times 2 1 \times 1 -3)。1111是 (-1 \times 8 7 -1)1000是 (-8)也就是为什么4位补码能表示的范围是[-8, 7]负数比正数多一个。这个公式是理解一切补码运算的钥匙。加法为什么直接加就行因为补码把负数也编码成了模 (2^n) 下的一个数减法变成加一个负数硬件上统一用加法器处理。那乘法呢如果你把两个补码按无符号数直接乘就等于忽略了最高位的负权值结果自然不对。补码乘法的所有实现技巧本质上都是在处理这个“负权值最高位”。1.2 乘法的真正难点位宽与截断乘法和加法最大的区别在于位宽。两个n位补码数相乘结果的完整表示需要2n位。比如4位补码能表示的最大正数7(7 \times 7 49)得用8位补码00110001才能装下。如果你非要把结果塞回4位那得到0001也就是1这显然不对。但如果你算的是(-1) \times (-1) 14位足够。所以“该用多少位存结果”取决于具体数值而不是统一规则——初学者最容易在这里踩坑。另一个坑来自截断。实际硬件和高级语言里乘法结果经常被截断到固定位数。截断的实质是取模 (2^n)。问题在于一个补码数经过取模之后它代表的正负可能完全变掉。比如8位补码里的11111111是-1截断到4位后变成1111仍然是-1数值没变但8位补码里的01111111127截断到4位变成1111-1数值面目全非。截断是否安全取决于目标位宽能不能容纳真实结果。这里可以借用矩阵乘法里的观点来打个比方。矩阵乘法有“行观点”和“列观点”行观点是结果矩阵的每个元素由左矩阵的一行和右矩阵的一列做内积得到列观点则是右矩阵的每一列是左矩阵对该列列向量的线性组合。二进制乘法的“竖式计算”也有两种看法行观点是把乘数按每一位拆开每位产生一个移位后的被乘数部分积最后全加起来列观点则是一次性排列所有部分积按列累加。补码乘法的符号扩展法本质就是先通过符号扩展把“负权值最高位”的问题转化成“无符号乘法”再用行观点把部分积加在一起。1.3 三条技术路线的总览工程上有三种主流做法各有适用场景符号扩展法先把两个操作数都符号扩展成2n位然后按无符号乘法直接算最后截取低2n位。这是软件实现和手工推导最稳妥的方法因为它的正确性最直观不容易出错。原码乘法先取绝对值按无符号乘法算最后根据两个操作数的符号位异或结果给结果补上符号位或取补码。这是早期硬件经常采用的方式缺点是先取绝对值需要额外逻辑而且有-8这种绝对值算不出来的特殊情况-8的绝对值8用4位表示不了。Booth算法直接在补码域里做乘法通过扫描乘数的连续“0/1”游程来减少部分积数量。这是现代硬件乘法器的主流实现也是很多计算机组成原理教材的必讲内容。下面几节我把这三条路线逐一拆开先讲最容易理解也最稳妥的符号扩展法再讲硬件里面真正用的Booth算法。2. 纯手算路线符号扩展法为什么最稳妥2.1 符号扩展的数学原理先回答一个问题为什么符号扩展之后按无符号乘法算结果就是对的设x和y都是n位补码数。把它们符号扩展成2n位后得到 (x) 和 (y)注意 (x) 作为2n位补码的值和x作为n位补码的值是相等的。符号扩展不改变数值这是补码最优雅的性质之一1011是-5符号扩展成11111011还是-5因为高位的1在8位补码里的总贡献正好等于−128 112 −16恰好补上原来4位里最高位从−8移到−128时多出的部分更严谨说扩展后的高位1加在一起数值上相当于多减了 (2^{2n-1})又多加回 (2^{2n-1} - 2^{n-1})净变化为零。现在把 (x) 和 (y) 都当成无符号2n位整数相乘得到的乘积在模 (2^{2n}) 意义下恰好等于两个2n位补码的乘积在补码下的编码。也就是无符号乘法器的位级结果和补码乘法器完全一致只要操作数已经符号扩展到足够的位宽。所以只要把两个n位操作数符号扩展到2n位做一次无符号乘法截取低2n位得到的就是正确的补码乘积。简单、直接、不需要任何修正逻辑。这正是为什么很多处理器指令集里MUL无符号乘和IMUL有符号乘在低半部分的结果是相同的区别只在高半部分。x86的32位模式下MUL用EDX:EAX装64位结果IMUL同样用EDX:EAX两个指令的低32位完全一样差别在EDX。这个特性经常被用来做“无符号乘取低32位当成有符号用”的等价替换原理就在上面这段。2.2 手算实例负乘正、正乘负、负乘负光讲理论不够我拿4位补码手算三个典型例子你跟着走一遍就通了。例子一((-5) \times 6 -30)-5 的4位补码是10116 的4位补码是0110。符号扩展成8位11111011和00000110。把00000110拆成 (2^1 2^2)所以乘积是11111011 \ll 1 11111011 \ll 2111110110 1111101100 ------------ 10111100010得到11位结果10111100010。注意无符号视角下这相当于 (251 \times 6 1506)。截取低8位11100010这是无符号的226。226在8位补码里是多少(226 - 256 -30)。结果对了-30。例子二(3 \times (-2) -6)3的4位补码0011-2的4位补码1110。符号扩展8位00000011 \times 11111110。无符号视角是 (3 \times 254 762)。二进制是101111101010位。截取低8位11111010这个是补码(250 - 256 -6)。正确。例子三((-3) \times (-3) 9)-3的4位补码1101符号扩展8位11111101无符号视角是 (253 \times 253 64009)这个数二进制是111110100010100116位。截取低8位00001001就是9。正确。三个例子跑完你可能会发现一个规律符号扩展法本质上是把负数映射到一个很大的“无符号替身”上乘完再通过截断把结果拉回正确的模意义下。整个过程不需要判断符号不需要修正这才是它稳健的原因。2.3 实战要点位宽怎么定、溢出怎么判符号扩展法在纸上算没问题落到代码和硬件时要确认三件事。第一扩展到位宽必须足够。n位乘n位至少扩展到2n位然后截取低2n位作为结果。扩展少了不行因为部分积之间的进位会从高位溢出把结果高位吃掉。扩展多了没关系只要最终截取的正确位宽保持不变即可。实际工程中我会先算好乘积的理论最大位宽比如两个8位补码相乘理论上是16位结果那就用一个16位信号承接绝不会拿8位去存。第二注意“负数比正数多一个”的问题。n位补码的负数是 (-2^{n-1}) 到 (-1)正数是 (1) 到 (2^{n-1}-1)。最极端情形是 ((-2^{n-1}) \times (-2^{n-1}) 2^{2n-2})这是一个正的 (2n-1) 位数值2n位补码能装下最大 (2^{2n-1}-1)但1位符号位2n-1位数值位刚好顶满。也就是说2n位乘法结果不会溢出这是补码乘法的一个关键性质——只要输出位宽是输入位宽的两倍就一定不会溢出。很多硬件乘法器设计正是利用这一点直接把两个n位输入接到2n位输出的乘法器上省掉溢出判断逻辑。第三判断“中间结果要不要截断”。如果你的算法是逐步移位累加中间累加器必须保持2n位宽度不能贪图省寄存器用n位否则进位丢了神仙都救不回来。我在FPGA上调试时遇到过好几次这种问题仿真波形看着某一步的值特别大然后下一步突然变成一个毫无关联的小数多半是中间累加器位宽不够被截断了。3. 硬件视角Booth算法与部分积的生成3.1 从竖式乘法到部分积阵列符号扩展法在软件和手工推导里很好用但对硬件不友好。原因在于两个n位数相乘会产生n个部分积每个部分积都要做符号扩展然后全部相加。n8的时候还好n64的时候64个64位甚至128位数相加面积和功耗都吃不消。所以硬件乘法器一般不直接做“竖式”而是想尽办法减少部分积数量。减少部分积的关键是乘数里连续的1可以合并处理。还记得小学学乘法时你用乘数的每一位去乘被乘数每个“1”都会产生一个部分积。但如果你把乘数看成二进制的“0/1串”一串连续的1比如011110可以等价看成 (100000 - 000010)也就是一个高位的加加一个低位的减。这样原本4个部分积变成2个。Booth算法的核心思想就是这个。3.2 一位Booth算法完整推导与实例标准的一位Booth算法也叫基2 Boothradix-2 Booth这样操作在乘数最低位右边额外添一位 (Q_{-1})初始为0。每一步看当前乘数最低位 (Q_0) 和它的右邻 (Q_{-1})(Q_0)(Q_{-1})操作含义00无操作连续的0不需要加01加被乘数M一个连续的1串开始加M10减被乘数M加-M的补码一个连续的1串结束减M11无操作连续的1中间不需要加每处理完一步把“累加器A 乘数Q 扩展位Q-1”这个组合整体算术右移一位然后重复n次。算术右移的意思是最高位补符号位而不是补0。我拿前面例子二的 (3 \times (-2)) 来走一遍完整流程被乘数 (M 3 0011)乘数 (Q -2 1110)n4初始 (A 0000)(Q_{-1} 0)。第一步(Q_00, Q_{-1}0)无操作。组合{A,Q,Q-1}为0000 1110 0算术右移一位变成0000 0111 0。此时 (A0000, Q0111, Q_{-1}0)。第二步(Q_01, Q_{-1}0)执行 (A A - M 0000 - 0011 1101)。右移组合1101 0111 0注意A的最高位是1所以右移时A的高位补1变成1110 1011 1。此时 (A1110, Q1011, Q_{-1}1)。第三步(Q_01, Q_{-1}1)无操作。右移1110 1011 1A的高位是1补1变成1111 0101 1。(A1111, Q0101, Q_{-1}1)。第四步最后一次移位完成(Q_01, Q_{-1}1)无操作。右移1111 0101 1变成1111 1010 1。此时 ({A,Q} 11111010)这是8位补码值为 (250 - 256 -6)。和预期一致。看到没最后的{A,Q}组合就是完整的2n位乘积。整个过程中没有“先取绝对值再算”的绕路也没有符号扩展一堆部分积的麻烦硬件只需要一个加法器/减法器、一个移位寄存器和一个有限状态机。Booth算法在实际硬件里通常用**基4radix-4**版本即一次看乘数的3位将部分积数量减少一半同时采用编码后的乘数位{-2, -1, 0, 1, 2}来控制被乘数的移位倍数。基4 Booth设计起来更复杂但思路完全一样。如果读者只是想做原型或者写作业一位Booth足够理解原理如果要做真正的芯片级乘法器建议直接上基4 Booth加Wallace树压缩那就是设计专题了。3.3 硬件实现避坑符号位扩展、倒数第二个部分积取反加一手写硬件乘法器的时候有三个坑我几乎每次都会踩一遍列出来给大家省点时间。坑一部分积的符号扩展。你按竖式把n个部分积列出来每个部分积在被乘数是负数的情形下本身是负数。不用等号的“短竖式”写法直接把有符号部分积按位相加是会出错的。正确做法是把每个部分积都符号扩展到2n位后再相加。一个常被忽视的简化技巧除了最后一个部分积其他部分积的符号扩展位可以只写作“所有扩展位都取符号位的值”而最后一个部分积的符号位位置需要特殊处理很多教材叫“符号位扩展技巧”本质上是用 (\bar{s}) 和 (s) 的组合来替代一串相同的符号位减少加数数量。坑二倒数第二个部分积的取反加一。如果你用的是“先取绝对值相乘再加符号”的原码法那么处理-8这种极端负数时取绝对值会溢出。这是原码乘法的一个死穴。如果你用的是直接补码乘法不是Booth那么涉及到“减法”操作时必须把减数取反加一这时候常见的错误是只取反不加一导致结果总差一个常数。记住乘法的减法和加法一样取反加一是一对操作不能拆。坑三算术右移还是逻辑右移。Booth算法的移位必须是算术右移也就是最高位补符号位。如果你在Verilog里用了逻辑右移正数没影响负数直接错乱。正确写法见第4.3节。这个坑特别隐蔽因为正数情况仿真全对一换负数就炸。4. 代码实现MATLAB、C语言与Verilog里的有符号乘法4.1 MATLAB十六进制转有符号数的三种写法MATLAB是数据解析的重灾区因为hex2dec默认返回的是无符号值。这点必须时刻警惕。搜热词里就有“matlab 16进制转有符号数”说明很多人卡在这。这里给出三种常用方法。最直接的方法是手动判断最高位% 方法一手动转换适合任意位宽 hexStr FE; % 8位的十六进制 dec hex2dec(hexStr); nBits 8; if dec 2^(nBits-1) signedVal dec - 2^nBits; else signedVal dec; end % signedVal -2第二种是利用typecast适合已经装进字节数组的情况% 方法二typecast 位级转换 bytes uint8(hex2dec(FE)); % 必须先转成uint8类型 signedVal typecast(bytes, int8); % signedVal -2第三种是直接用 Fixed-Point Designer 工具箱的fi对象% 方法三fi对象适合带小数位和自定义位宽 fiVal fi(hex2dec(FE), 1, 8, 0); % 1表示有符号8位字长0位小数 % fiVal -2我最推荐方法一因为它改位宽只需改nBits不依赖工具箱也不依赖平台字节序。typecast有个隐性坑它完全按内存内容解释如果你把数据先转成了double那内存内容变了typecast会得到垃圾值所以必须保证输入是整型。这一点新手特别容易踩。MATLAB里做有符号乘法还有个重要细节int8(3) * int8(254)会报错吗不会MATLAB的整数乘法不会静默溢出它会在溢出时饱和到该类型上下限。比如int8(100) * int8(100)返回int8(127)而不是截断成某个补码值。这个行为和C语言完全不同。所以如果你想要的是“补码截断”效果需要自己用typecast或者fi对象来模拟。做数据解析时先确认MATLAB版本的行为再决定用哪条路。4.2 C语言整型提升与隐式溢出C语言的整数算术有个著名的规则叫“整型提升”integer promotion。简单说int8_t、int16_t在做算术运算时会先被提升成int通常是32位再运算结果也是int。这意味着int8_t乘int8_t实际上是用32位乘法器做的然后赋值给int8_t时再截断。这种设计在int8_t场景下反而很安全因为8位乘8位的最大乘积6553632位完全装得下截断到8位时如果正好在范围内就没问题。真正的坑在int32_t上。32位乘32位结果需要64位但整型提升后还是在32位int里运算溢出是未定义行为。编译器可能给你截断结果可能优化掉整段代码表现完全不可预测。这是一个真实性事故——很多做协议解析的程序算时间戳差值或者乘法时用了long结果在某个平台上一夜之间全是负数。正确的写法是显式转换到64位#include stdint.h int32_t a -100000; int32_t b 300000; // 错误int32_t相乘会先提升为int32位溢出未定义 // int64_t result a * b; // 正确先转换为int64_t再乘 int64_t result (int64_t)a * b;赋值阶段也有讲究。看这个例子int8_t a -5; int8_t b 6; int8_t c a * b; // a*b作为int是-30赋值给int8_t没溢出结果-30 int8_t d -8 * -8; // int运算结果是64赋值给int8_t没问题结果64不对-8 * -8 64int8_t的范围是 -128 到 12764放得下没问题。但如果你写int8_t e 127 * 127;int结果是16129赋值给int8_t时溢出结果是未定义的但大多数编译器会给出截断值1。这个截断行为在C标准里是“实现定义”的不是“未定义”所以不同编译器表现可能不同。写代码时务必要让目标类型能装下结果或者干脆用int64_t中间承接。这里我推荐一个经验法则只要结果位宽可能超过当前类型一律先升级类型再运算永远不要依赖“这段代码只在XX编译器上跑”的侥幸。库函数的返回值、结构体字段、协议解析里的长度字段都是溢出高发区。x86汇编层面的细节也值得知道。MUL是无符号乘法IMUL是有符号乘法。两条指令在低半部分结果完全相同高半部分不同。编译器生成int32_t乘法代码时会自动选择合适的指令。但如果你写内联汇编或者做底层移植务必搞清楚当前操作是有符号还是无符号以及结果要取多宽。4.3 Verilog$signed与手写乘法器FPGA和ASIC设计里的有符号乘法最标准的做法是直接使用*运算符但要给它正确的“符号性”。Verilog-2001 引入了$signed()系统函数可以用来标记一个信号为有符号module signed_mult #( parameter WIDTH 8 )( input logic [WIDTH-1:0] a, // 虽然声明是无符号但会被用作有符号 input logic [WIDTH-1:0] b, output logic [2*WIDTH-1:0] p ); wire signed [WIDTH-1:0] a_s $signed(a); wire signed [WIDTH-1:0] b_s $signed(b); assign p a_s * b_s; // 2*WIDTH位结果正确 endmodule注意两个细节。第一乘法结果的位宽必须是2*WIDTH否则综合工具会截断。第二如果你想手动搭Booth算法或部分积阵列移位必须用算术右移并且要保证所有累加寄存器是有符号类型// 算术右移示例 logic signed [2*WIDTH-1:0] acc; assign acc acc 1; // 是有符号算术右移是逻辑右移手工搭Booth乘法器的时候状态机每拍完成一次“加减右移”总共要WIDTH拍。寄存器声明为signed可以简化符号处理。还有一点在SystemVerilog里logic signed和wire signed都能用但bit类型不能有符号这算是最常见的语法坑。另外综合工具对*运算符的优化程度远高于手写乘法器。如果你不是专门做芯片底层库老老实实用*让综合器去推断DSP单元比手撸Booth靠谱得多。只有在你需要控制延迟、面积、功耗或者做大规模矩阵乘法阵列不得不手工布局时才值得手工搭建乘法器。这一点我认为特别重要能交给工具的事别自己硬扛。5. 常见问题与排查技巧实录5.1 症状、原因、排查方法速查表我把这些年遇到的有符号乘法问题整理了张速查表有类似症状直接对照症状可能原因排查方向负数变为很大的正数解析时没有做符号扩展把补码当无符号检查hex2dec/byte转int路径手动判断最高位乘法结果刚好差一个常数取反加一少做了一步重点检查减法操作确认加法器/减法器是否配套正数乘法对一遇负数就乱部分积符号扩展没做或做错把所有部分积都符号扩展到2n位再相加仿真正确上板错误数据路径位宽不够进位被截断检查中间累加器位宽建议至少2n位MATLAB里结果被截到上限整数运算饱和而非补码截断改用fi对象或typecast手动处理C语言里乘法结果随机int32_t×int32_t溢出未定义行为显式转换为int64_t再乘Verilog仿真负数全变正用了逻辑右移而不是换成算术右移并把寄存器声明为signedBooth算法结果中间对、最后错最后一次移位多余/缺失检查循环边界Booth算法需要n次移位后才能输出5.2 数据解析中的“符号扩展”陷阱一个完整案例再说回文章开头的MATLAB案例。那位朋友报文的温度字段是一个字节比如0xFE代表-2摄氏度。他的代码是这样写的raw hex2dec(FE); % 得到 254 temp raw * 0.1; % 得到 25.4完全不对修正之后raw hex2dec(FE); if raw 128 raw raw - 256; % raw 变成 -2 end temp raw * 0.1; % 得到 -0.2正确这个raw 128的判断本质上就是最高位为1就减 (2^n)等价于前面补码公式的逆运算。对于任意位宽的数据字段通用的写法是这样的function s signedFromHex(hexStr, nBits) u hex2dec(hexStr); if u 2^(nBits-1) s u - 2^nBits; else s u; end end只要是2的幂次位宽这个方法永远成立。要是带小数的定点数还需要额外除以 (2^f)其中f是小数位位数。除此之外C语言里解析网络报文也经常需要手动符号扩展比如一个协议字段是12位有符号数你得先取出这12位再判断第11位是否为1是的话就把高20位全部填1。很多Bug就藏在这些“手动扩展”里。5.3 仿真波形怎么看定位乘法Bug的实操技巧最后分享一个调试经验。FPGA或软件仿真里定位乘法问题比定位加法难因为乘法错误的“样式”更多样。我的习惯是三步走。第一步先分两步验证无符号乘法是否正常。把两个输入都改成正数跑一遍确认基本乘法通路没问题。正数下补码计算和无符号完全一致所以这一步能把“乘法本身的问题”和“符号处理的问题”分离开。第二步打印中间所有部分积。如果你是手写阵列乘法器把每个部分积的位宽、符号扩展后的值、累加值全都打出来。我之前碰到一个案例某一位部分积因为取错信号符号扩展位全成了0导致负数相乘时结果差一个很大的2的幂次。打印部分积后一眼就发现了。第三步用基准值交叉验证。写一个函数输入相同的a、b用64位整数计算正确结果再与你的FPGA仿真结果做全组合比对。全组合在小位宽比如8位时只有256×25665536种可能仿真器完全跑得动。这个测试向量跑通基本可以宣布乘法器代码没问题。这三步做完90%的乘法Bug都已经现形了。剩下的10%大概率是异步时序或复位问题那就得用调试器看波形了。6. 几个容易被忽略的扩展场景聊到有符号乘法和补码有几个外围概念容易被忽略但它们在实际工程里经常和乘法纠缠在一起。6.1 浮点数乘法与补码的关系IEEE 754浮点数的乘法尾数部分本质上是定点补码乘法。浮点数相乘时符号位作异或运算阶码相加尾数相乘。尾数乘法占用的是浮点乘法器的大部分面积而它处理的就是无符号定点数通常在IEEE格式里尾数是原码表示。理解整数补码乘法对理解浮点乘法器的低层实现很有帮助。如果哪天你看到浮点乘法结果出现规律性偏差除了舍入也可以怀疑一下尾数乘法是否有符号扩展问题。6.2 矩阵乘法里的行观点与列观点前面提到过矩阵乘法可以按“行观点”也可以按“列观点”来理解。这个类比放到二进制乘法里非常贴切a × b的行观点是把b按二进制位拆开每位贡献一个“a左移若干位”的部分积最后求和列观点则是把所有部分积纵向排列按列统一累加进位。硬件里的Wallace树压缩树本质就是列观点的加速实现——它不去逐行累加而是把每一列的总和直接压缩成两行再相加。理解行、列两个视角对看乘法器架构论文会很有帮助。6.3 负数乘法在DSP和AI推理中的特殊处理做DSP数字信号处理和AI推理部署的同学请注意很多嵌入式芯片的DSP指令支持“饱和乘法”和“舍入乘法”它们不是简单的截断。比如ARM的SMULxy指令做16位有符号乘法结果直接是32位而某些DSP做两个Q15数相乘时结果左移一位再舍入以避免双符号位。这里的“双符号位”guard bit概念本质就是乘法结果位宽扩展的一种形式。如果算法仿真的浮点模型和定点模型在你处理负数时出现微小偏差多半是乘法后的舍入策略不同而不是补码本身有错。最后分享一个小技巧补码乘法说到底是“模算术”的一个实例。理解这一点很多看起来玄乎的行为都有了解释截断就是取模溢出就是模回绕符号扩展就是保值的位宽升级。这就像学做菜理解了食材的特性不需要背菜谱也能临场发挥。我个人在写完代码之后习惯性会做两步自查。第一步拿边界值去测最大值乘最大值、最小值乘最小值、最大值乘最小值这三组如果结果都对99%的边界情况都稳了。第二步如果代码里有符号扩展相关的位操作一定写注释标注“这里是因为补码最高位是负权值所以要扩展符号位”防止三个月后的自己看着代码发呆。从手算符号扩展到Booth算法硬件实现再到MATLAB、C、Verilog三种语言的落地补码乘法这条线到这里算是一条龙走完了。剩下的就是多写几个例子亲手验一遍负数的各种组合等这些细节都变成肌肉记忆你再看“有符号数乘法”这个题目就会觉得它就像一加一那么简单了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

从一包镧铈掺杂 YAG 粉到论文定稿:稀土人的 AI 工具搭子怎么选? 2026/10/2 21:03:14

从一包镧铈掺杂 YAG 粉到论文定稿:稀土人的 AI 工具搭子怎么选?

先说结论:如果你在搜“专业 AI 论文生成工具排行榜”,别只看谁排在第一。对工学 / 材料类 / 稀土材料科学与工程的同学来说,真正好用的方式不是找一个“一键写完毕业论文”的神器,而是按任务环节搭一套组合拳。 我拿一个很典型的本…

阅读更多 →
STM32+W5500实现Profinet从站:硬件电路与协议栈移植实战 2026/10/2 21:03:08

STM32+W5500实现Profinet从站:硬件电路与协议栈移植实战

STM32和W5500这对组合,在嵌入式网络通信里算是经典搭档了。最近不少做工业设备的朋友都在问W5500能不能直接上profinet,还有人在折腾车载以太网、伺服驱动这些场景,我觉得可以把手头这个项目的实战经验完整梳理一遍,从硬件电路到软…

阅读更多 →
医美行业还在增长为何机构不再敢开店_2026洞悉报告深度解读 2026/10/2 21:03:07

医美行业还在增长为何机构不再敢开店_2026洞悉报告深度解读

医美行业还在增长,为什么机构不再敢开店?——读《2026中国医美行业年度洞悉报告》 导读:德勤中国与艾尔建美学预计,2030 年中国医美消费市场规模有望突破 6000 亿元;与此同时,报告观察到机构净增放缓、受访…

阅读更多 →
别让 AI 替你做设计:视传博士论文从开题到包装原型的工具分工 ✏️ 2026/10/2 21:03:07

别让 AI 替你做设计:视传博士论文从开题到包装原型的工具分工 ✏️

如果你是视觉传达设计方向的博士生,大概很熟悉这种“分裂感”: 一边要做设计实践——海报、包装、字体、品牌系统、交互原型;另一边又要写博士论文,得把视觉决策放进理论框架、用户研究和实验数据里。很多同学的卡点不是“不会做设…

阅读更多 →
ML4W 深度定制指南:基于 starter 模板打造属于自己的 Waybar 主题并一键切换 2026/10/2 21:03:01

ML4W 深度定制指南:基于 starter 模板打造属于自己的 Waybar 主题并一键切换

桌面应用 【免费下载链接】dotfiles The ML4W OS - Dotfiles for Hyprland - An advanced and full-featured configuration for the dynamic tiling window manager Hyprland. Ready to install for Arch Linux, Fedora and openSuse. 项目地址: https://gitcode.c…

阅读更多 →
claude-opus-5.5 调用一直报 529 overloaded 怎么办?sonnet-5.5 同样并发却没事——排查思路 + 指数退避重试代码(Python/Node) 2026/10/2 21:03:01

claude-opus-5.5 调用一直报 529 overloaded 怎么办?sonnet-5.5 同样并发却没事——排查思路 + 指数退避重试代码(Python/Node)

claude-opus-5.5 调用一直报 529 overloaded 怎么办?sonnet-5.5 同样并发却没事——排查思路 指数退避重试代码(Python/Node) 上周三我在跑一个批量代码的 pipeline,用的 claude-opus-5.5,并发 8 个请求,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉