汇编语言中如何通过移位操作实现非2的幂次整数除法
汇编中用移位组合实现常数除法的方法
当然有和乘法对应的移位类指令组合实现除法的方案,早年没有硬件除法指令的架构(比如经典ARMv4/ARMv5)全靠这套方案做整数除法,性能比软件逐位模拟除法快得多。
不过除法的实现逻辑比乘法稍复杂:你之前用的乘3实现,本质是把乘数拆成2的整数次幂的和(3=2+1),所以拆成移位+加法就能做。但除数如果不是2的整数次幂,它的二进制倒数是无限循环小数,没法拆成有限个2的负整数次幂的和,所以没法像乘3那样用两三条指令搞定全范围的精确计算。
实际工程里通用的方案是定点倒数乘法:除以常数N等价于乘以N的定点放大版倒数,最后做移位修正,过程只需要少量乘法、移位、加减指令,不需要调用除法指令。
举个例子:32位无符号数除以3(对应你说的9/3场景)
编译器生成的标准实现用的是0x55555556作为魔数——这个值是1/3放大2^33倍后的定点整数,代码如下:
; 输入:r0 = 待计算的无符号整数,比如示例中的9 ; 输出:r0 = r0 / 3 的无符号商 ldr r1, =0x55555556 ; 加载1/3的33位定点倒数 umull r2, r1, r0, r1 ; 做无符号32位乘法,结果高32位存r1,低32位存r2 mov r0, r1, lsr #1 ; 高32位逻辑右移1位,得到最终商
代入r0=9计算:9和0x55555556相乘的高32位值是6,右移1位刚好得到3,结果完全正确。
如果你的场景要求完全不用乘法指令、且待计算的数值范围很小(比如0~100),也可以用多次移位+加法逐次逼近1/3的值,本质是把无限循环的二进制倒数截断到需要的精度,不过这种方案代码更长,适用范围窄,实际很少用。
几个注意点
- 你之前了解的用LSR实现除2,本质是这套方案的特例:除以2的倒数是0.5,放大2^1倍就是1,乘完右移1位刚好对应单条LSR指令。
- 这套方案仅对除数为常数的场景性价比最高,如果除数是运行时可变的变量,还是需要用通用除法逻辑。
- 如果处理的是有符号数,对应的魔数、移位偏移、舍入修正逻辑都要调整,避免负数计算出错。
- 日常写汇编不用自己手算魔数,开启优化的编译器会自动把常数除法替换成最高效的移位+加减+短乘法组合,不需要手动写DIV指令。
内容的提问来源于stack exchange,提问作者Sarmad
相关产品推荐
相关产品推荐

