NASM 64位编程:shl al与shl ax导致移位相加乘法结果异常
这个问题我太熟悉了——本质是你没搞清楚移位相加乘法里被乘数的位数扩展需求,shl al,1和shl ax,1的核心区别就在于是否能保留移位产生的进位!
问题根源:8位寄存器移位无法保存高位进位
移位相加乘法的核心逻辑是:被乘数每次左移1位(等价于×2),乘数每次右移1位,当乘数最低位为1时,把当前的被乘数累加到结果中。
这里的关键是:8位的被乘数左移7次后,会变成15位的数,必须用16位寄存器来完整保存它的所有位。你用shl al,1的时候,只操作了8位的al寄存器——当al的最高位(第7位)是1时,左移会产生进位,但这个进位只会放到CF标志位里,不会自动存入ah(ax的高8位),导致被乘数的高位丢失,实际值并没有正确翻倍。
用你的例子拆解错误过程
拿a=12(0xC)、b=10(0xA,二进制1010)来说:
- 当循环到第5次时,
al已经被左移4次变成0xC0(192),此时再执行shl al,1:al的最高位是1,左移后al变成0x80(128),CF=1,但ah还是0,所以ax的值是0x0080,但实际应该是0x0180(384)——这里直接丢了256的数值!
- 后续累加的时候,你用的是错误的
ax值,最终结果自然会比正确值小很多,就出现了得到20而不是120的情况。
而shl ax,1操作的是整个16位寄存器,左移时al的进位会直接进到ah的最低位,完美保留了被乘数的完整值,每次左移都正确实现了“×2”的操作。
修正后的代码示例
section .data a db 12 ; 8位被乘数 b db 10 ; 8位乘数 result dw 0 ; 16位结果存储位置 section .text global _start _start: mov al, [a] ; 加载被乘数到al mov bl, [b] ; 加载乘数到bl xor dx, dx ; 初始化结果寄存器dx为0 mov cx, 8 ; 循环8次(处理8位每一位) multiply_loop: test bl, 1 ; 检查乘数最低位是否为1 jz skip_add ; 最低位为0,跳过累加 add dx, ax ; 最低位为1,将当前被乘数(ax)加到结果dx skip_add: shl ax, 1 ; 被乘数左移1位(×2),用ax保留进位 shr bl, 1 ; 乘数右移1位 loop multiply_loop ; 循环直到cx为0 mov [result], dx ; 保存最终结果 ; 程序退出(Linux系统调用) mov eax, 1 xor ebx, ebx int 0x80
关键总结
- 移位相加乘法中,被乘数需要用比自身多一倍位数的寄存器存储,才能保证移位时不丢失高位进位。
- 8位被乘数必须用16位的
ax来存储和移位,而不能只用8位的al——这就是你换指令后结果错误的核心原因。
内容的提问来源于stack exchange,提问作者asn
相关产品推荐
相关产品推荐

