将这段C代码转换成汇编,以每次移动一位来执行除法。

huangapple go评论62阅读模式
英文:

Converting this C Code to Assembly to do Division shifting one bit at a time

问题

以下是您提供的C代码和更新后的汇编代码的翻译部分:

C代码:

我正在尝试将此C代码转换为汇编,以执行除法而不使用除号。已经证明C代码有效。我已经尽力将C代码转换为汇编。当除数为1或分子为0时,它将正确工作,但对于诸如5789217 / 810 / 5等除法将不起作用。这些情况下的输出始终是商为0,余数为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 {
        // 根据提示,运行31次迭代
        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 # 之前是ecx
    movl dividend, %ebx
    movl $31, %ecx # 之前是esi
    # 第一个if语句检查除数是否为1
    cmpl $1, %esi
    jne for_start

    # 除数为1,所以商是被除数,余数为0
    movl %ebx, %eax
    xor %edx, %edx
    jmp done

for_start:
    cmpl $0, %ecx
    jle done

    # FOR循环内部:

    shll $1, %edx        # 左移余数
    movl %ebx, %edi      # 复制被除数
    shrl %cl, %edi       # 右移复制
    and $1, %edi         # 与余数做与运算
    orl %edi, %edx       # 或运算余数和商 CHECK THIS
    # 开始if语句

    cmpl %esi, %edx       # 比较余数和除数
    jle next_iter         # 如果余数小于除数,则跳过减法

    sub %esi, %edx        # 从余数中减去除数
    movl $1, %edi
    shll %cl, %edi      # 将商左移cl位
    orl %edi, %eax     # 或运算商和1

next_iter:
    decl %ecx             # 减小循环计数器
    jns for_start

    # FOR循环结束

done:

    nop
英文:

I'm trying to convert this C Code to Assembly to do division without using the division sign. The C Code has been proven to work. I've finished converting the C Code to Assembly to the best of my ability. It will work correctly when dividing by 1 or when 0 is in the numerator, but will not work for division such as 5789217 / 8, 10 / 5, etc. The output for these is always that the quotient is 0 and the remainder is 0.

I tried making some minor tweaks here and there but ultimately im not sure where the assembly code is going wrong.

Here's the C Code:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
 
int main (int argc, char *argv[]){
 
    //first command line argument is the dividend
    long long dividend = atoll(argv[1]);
    //second command line argument is the divisor
    long long divisor = atoll(argv[2]);
    //we need the remainder 
    long long remainder = 0;
    //we need the quotient
    long long quotient = 0;

    if (divisor == 1) {
        quotient = dividend;
        remainder = 0;
    } else {
        //runs for 31 iterations as stated in the prompt 
        for(int i = 31; i &gt;= 0; i--){
            remainder &lt;&lt;= 1;
            remainder |= (dividend &gt;&gt; i) &amp; 1;
            if (remainder &gt;= divisor){
                remainder -= divisor;
                quotient |= 1 &lt;&lt; i;
            }
        }
    }
 
    //print the results
    printf(&quot;%lld / %lld = %lld R %lld\n&quot;, dividend, divisor, quotient, remainder);
 
    return 0; 
}

Here is the UPDATED Assembly code:

.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 
    # first if statement that checks if the divisor is 1
    cmpl $1, %esi
    jne for_start

    # divisor is 1, so quotient is dividend and remainder is 0
    movl %ebx, %eax
    xor %edx, %edx
    jmp done

for_start:
    cmpl $0, %ecx
    jle done

    # INSIDE FOR LOOP:

    shll $1, %edx        # shift remainder left
    movl %ebx, %edi      # copy dividend
    shrl %cl, %edi        # shift copy right
    and $1, %edi         # and 1 with the remainder
    orl %edi, %edx       # or the remainder and quotient CHECK THIS
    # start of if statement

    cmpl %esi, %edx       # compare the remainder and the divisor
    jle next_iter         # if remainder is less than divisor, skip subtraction

    sub %esi, %edx        # subtract divisor from remainder 
    movl $1, %edi
    shll %cl, %edi      # Shift quotient left by cl
    orl %edi, %eax     # Or quotient with 1

next_iter:
    decl %ecx             # decrement loop counter
    jns for_start

    # END OF FOR LOOP

done:

    nop

答案1

得分: 1

以下是翻译好的部分:

  1. Example assembly code is in this github gist. (I had written it some months ago. Time to publish.) I'm certain if somebody stares at my algorithm long enough they'll find a way to tear it to shreds. I finally got one working on my own though.

  2. Your algorithm is completely differently though.

  3. The problem with your asm translation of your alrogithm is here:

    shll $1, %edx        # shift remainder left
    shrl $1, %ebx        # shift dividend right
    and $1, %edx         # and 1 with the remainder
  1. After the first loop, %edx is either 0 or 1. After the second loop it must be 0.

  2. It's like it wants to read

    shll $1, %edx        # shift remainder left
    movl %ebx, %edi      # copy dividend
    shrl %esi, %edi        # shift copy right
    and $1, %edi         # and 1 with the remainder
  1. but that doesn't work because that shift instruction doesn't exist. Redo register allocation so that i is in %cl instead. (Put the divisor in %esi or something like that.)
    shll $1, %edx        # shift remainder left
    movl %ebx, %edi      # copy dividend
    shrl %cl, %edi        # shift copy right
    and $1, %edi         # and 1 with the remainder
英文:

Example assembly code is in <A HREF="https://gist.github.com/joshudson/88570993c7863e4ba83af5fef61af2ad">this github gist</A>. (I had written it some months ago. Time to publish.) I'm certain if somebody stares at my algorithm long enough they'll find a way to tear it to shreds. I finally got one working on my own though.

Your algorithm is completely differently though.

The problem with your asm translation of your alrogithm is here:

    shll $1, %edx        # shift remainder left
    shrl $1, %ebx        # shift dividend right
    and $1, %edx         # and 1 with the remainder

After the first loop, %edx is either 0 or 1. After the second loop it must be 0.

It's like it wants to read

    shll $1, %edx        # shift remainder left
    movl %ebx, %edi      # copy dividend
    shrl %esi, %edi        # shift copy right
    and $1, %edi         # and 1 with the remainder

but that doesn't work because that shift instruction doesn't exist. Redo register allocation so that i is in %cl instead. (Put the divisor in %esi or something like that.)

    shll $1, %edx        # shift remainder left
    movl %ebx, %edi      # copy dividend
    shrl %cl, %edi        # shift copy right
    and $1, %edi         # and 1 with the remainder

huangapple
  • 本文由 发表于 2023年5月14日 08:50:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76245406.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定