You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

逐位移位除法C代码转汇编异常求排查

逐位移位除法汇编代码调试问题

我正在把一段无需除法运算符的逐位移位除法C代码转成汇编,C代码已验证可正常运行,但汇编仅在除数为1或被除数为0时结果正确,像5789217/8、10/5这类运算,商和余数始终为0,多次调试仍未定位问题。

原C代码

#include <stdio.h>
#include <stdlib.h>
 
int main (int argc, char *argv[]){ 
    // 第一个命令行参数是被除数
    long long dividend = atoll(argv[1]);
    // 第二个命令行参数是除数
    long long divisor = atoll(argv[2]);
    long long remainder = 0;
    long long quotient = 0;

    if (divisor == 1) {
        quotient = dividend;
        remainder = 0;
    } else {
        // 循环32次(i从31到0)
        for(int i = 31; i >= 0; i--){
            remainder <<= 1;
            remainder |= (dividend >> i) & 1;
            if (remainder >= divisor){
                remainder -= divisor;
                quotient |= 1 << i;
            }
        }
    }
 
    printf("%lld / %lld = %lld R %lld\n", dividend, divisor, quotient, remainder);
    return 0; 
}

问题汇编代码

.global _start

.data
dividend:
    .long 100
divisor:
    .long 100
quotient:
    .long 0
remainder:
    .long 0

.text
_start:
    movl quotient, %eax
    movl remainder, %edx
    movl divisor, %esi # previously ecx
    movl dividend, %ebx
    movl $31, %ecx # previously esi 
    # 判断除数是否为1
    cmpl $1, %esi
    jne for_start

    # 除数为1时,商等于被除数,余数为0
    movl %ebx, %eax
    xor %edx, %edx
    jmp done

for_start:
    cmpl $0, %ecx
    jle done

    # 循环体开始:
    shll $1, %edx        # remainder左移1位
    movl %ebx, %edi      # 复制被除数到edi
    shrl %cl, %edi        # 被除数右移i位(cl存当前i值)
    and $1, %edi         # 取最低位
    orl %edi, %edx       # 将该位并入remainder
    # if判断开始

    cmpl %esi, %edx       # 比较remainder和divisor
    jle next_iter         # 如果remainder < divisor,跳过减法

    sub %esi, %edx        # remainder -= divisor
    movl $1, %edi
    shll %cl, %edi      # 1左移i位
    orl %edi, %eax     # 将该位并入quotient

next_iter:
    decl %ecx             # 循环计数器减1
    jns for_start         # 非负则继续循环

done:
    nop

问题分析与修复

核心问题:循环次数缺失

原C代码循环32次(i从31到0,包含0),但汇编代码的for_start标签下先判断cmpl $0, %ecx,当ecx=0时会直接跳转到done,跳过了i=0的循环体执行,导致整整一位数据未被处理,最终结果错误。

修复方案

移除for_start处的循环前置判断,让循环先执行体再判断是否继续,确保i从31到0的所有32次循环都被执行:

.global _start

.data
dividend:
    .long 100
divisor:
    .long 10
quotient:
    .long 0
remainder:
    .long 0

.text
_start:
    movl quotient, %eax
    movl remainder, %edx
    movl divisor, %esi
    movl dividend, %ebx
    movl $31, %ecx 
    # 判断除数是否为1
    cmpl $1, %esi
    jne for_start

    # 除数为1时,商等于被除数,余数为0
    movl %ebx, %eax
    xor %edx, %edx
    jmp done

for_start:
    # 先执行循环体,再处理计数
    shll $1, %edx        # remainder左移1位
    movl %ebx, %edi      # 复制被除数到edi
    shrl %cl, %edi        # 被除数右移i位(cl存当前i值)
    and $1, %edi         # 取最低位
    orl %edi, %edx       # 将该位并入remainder

    cmpl %esi, %edx       # 比较remainder和divisor
    jle next_iter         # 如果remainder < divisor,跳过减法

    sub %esi, %edx        # remainder -= divisor
    movl $1, %edi
    shll %cl, %edi      # 1左移i位
    orl %edi, %eax     # 将该位并入quotient

next_iter:
    decl %ecx             # 循环计数器减1
    jns for_start         # 当ecx >=0时继续循环(直到ecx变为-1时退出)

done:
    # 添加退出系统调用,避免程序崩溃
    movl $1, %eax
    xor %ebx, %ebx
    int $0x80

额外优化点(可选)

原C代码使用long long(64位)类型,但汇编用的是32位.long和32位寄存器。如果需要完全适配64位运算,需:

  • 将数据段的.long改为.quad
  • 使用64位寄存器(rax、rdx、rbx、rsi、rcx、rdi)
  • 将循环初始值改为$63(覆盖64位所有位)
  • 移位指令改用64位版本(shlq、shrq)

内容的提问来源于stack exchange,提问作者user21894767

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 02:17:40