【问题标题】:How do I divide two numbers in assembly without using the MUL or DIV operators?如何在不使用 MUL 或 DIV 运算符的情况下在汇编中划分两个数字?
【发布时间】:2016-12-07 02:15:31
【问题描述】:

我正在研究一种可以在不使用 MUL 或 DIV 运算符的情况下在汇编中将两个数字相除的东西。逻辑是,如果 n*var

首先,我创建了一个利用位移的乘法器,这很有效。

//multiply
int ans, var1;
_asm {
        mov eax, 00001111b; A = 1111
        mov ebx, 00001010b; B = 1010
        mov var1, 00000001b; C = 0001
        mov ecx, 0;
        shl ebx, 3; shift b register left by 3
    L1:
        cmp ecx, 4; check if multiplication is done
            je L4; go to empty loop
        inc ecx; increment counter by 1
        AND var1, eax; check if the least significant bit is 1
        cmp var1, 1; checks if var1 is = 1
            je L2;
        cmp var1, 0; check if var1 = 0
            je L3;
    L2:
        shr eax, 1; shift a register right 1 bit
        add eax, ebx; add a and b registers
        jmp L1; go back to L1
    L3:
        shr eax, 1;
        jmp L1; go back to L1
    L4 :
        mov ans, eax; move eax to answer
};
cout << "Multiply 00001111 and 00001010" << endl << ans << endl;

所以我要做的是用另外两个循环来实现这个乘法器:一个增加 n 并将寄存器设置为正确的数字以进行相乘,另一个检查 nvar >= x。如果 nvar

这个程序有一些我没有看到的错误,不知何故,答案总是 var2*16,其中 var2 是要除的数字。

int var1;
int var2;
int var3;
int d;
_asm {
    ; if n*var3 < var2, increment n and repeat
    mov var2, 12; number to be divided
    mov var3, 5; number to divide with
    mov ecx, 0;
    mov edx, 0; number to increment
L11 :
    cmp eax, var2;
    js L12; if n*n < var2, increment n and multiply.
    cmp eax, var2
    jns L16; answer found, jump to empty loop       
L12 :
    inc edx
    mov eax, edx
    mov ebx, var3
    shl ebx, 3
    mov ecx, 0
    jmp L13
L13 :
    cmp ecx, 4; check if multiplication is done
    je L11;
    inc ecx; increment counter by 1
    AND var1, eax; check if the least significant bit is 1
    cmp var1, 1; checks if var1 is = 1
    je L14;
    cmp var1, 0; check if var1 = 0
    je L15;
L14:
    shr eax, 1; shift a register right 1 bit
    add eax, ebx; add a and b registers
    jmp L13; go back to L1
L15 :
    shr eax, 1;
    jmp L13;
L16:
    mov d, edx
};
cout << "The quotient is " << d << endl;

我也在创建一个非常相似的程序来计算一个数字的平方根,如果 n*n

我已经被这个问题困扰了几天,我无法弄清楚是什么问题导致了这个奇怪的错误。任何帮助表示赞赏。

【问题讨论】:

  • 使用调试器单步调试代码,看看它在哪些地方没有做你想做的事情。
  • 前段时间,我用纯 asm 编写了一个小型波兰语计算器。它工作,至少对于整数。尽管如此,整数除法的结果也是小数,这似乎也有效。只要你知道,那个除法就是重复减法,你可以用 sub 来做。我就是这样做的。
  • 在第二个代码块中:eax 第一次经过L11: 是什么?对我来说看起来不确定。再加上用js/jns 两次做同样的cmp 是在浪费CPU 周期。在第一个cmp + js 指令对之后,您不需要再次执行cmp,因为标志仍然从以前设置(然后js 不会修改标志)并且jns 将在100% 的情况下发生,因为之前的js 已经处理了所有 SF=1 的案例。 ...我确实只检查了第二块的那〜10 行,因为我很好奇你是否真的++n 然后乘以n*var AGAIN,OMG :D .. 荒谬。 (n+1)*var == (n*var)+var!
  • 你的乘数坏了。它仅在 a 小于 2 的幂时起作用。您的测试用例恰好是您没有注意到的幸运值之一。
  • 有一种更有效的平方根算法,它不是尝试每个可能的值,而是从平方根作为0 开始并尝试一次设置一位,从最高可能位向下。如果此测试值的平方为 0x8000,下一次尝试将是 0xC0000x0400,具体取决于第一次尝试的结果,等等。

标签: assembly x86


【解决方案1】:

减法可以回答您的问题(“如何在不使用 MUL 或 DIV 运算符的情况下在汇编中划分两个数字?”),例如(在 VS2013 上测试):

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(void){
    int dividend;
    cout << "Enter DIVIDEND : "; //◄■ EXAMPLE : 23.
    cin >> dividend;

    int divisor;
    cout << "Enter DIVISOR : "; //◄■ EXAMPLE : 4.
    cin >> divisor;

    int quotient = 0;
    _asm {
            mov eax, dividend
        divisions:
            cmp eax, divisor
            jb  finish        //◄■ IF DIVIDEND < DIVISOR
            sub eax, divisor  //◄■ DIVIDEND - DIVISOR. EXAMPLE :  23 - 4.
            inc quotient      //◄■ DIVISIONS COUNTER.
            jmp divisions
        finish:
    }
    cout << "The result is " << quotient; //◄■ EXAMPLE : 5
    return 0;
}

之前的代码会抛出整数结果。如果您想要带小数的结果,则需要将最后一个被除数(上一个示例中的 3)乘以 10 获得一位小数或乘以 100 获得两位小数(或 1000 获得三位小数,依此类推),例如(在 VS2013 上测试) :

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(void){
    int dividend;
    cout << "Enter DIVIDEND : "; //◄■ EXAMPLE : 23.
    cin >> dividend;

    int divisor;
    cout << "Enter DIVISOR : "; //◄■ EXAMPLE : 4.
    cin >> divisor;

// ▼ CALCULATE QUOTIENT.
    int quotient = 0;
    _asm {
            mov eax, dividend
        divisions:
            cmp eax, divisor
            jb  finish1       //◄■ IF DIVIDEND < DIVISOR
            sub eax, divisor  //◄■ DIVIDEND - DIVISOR. EXAMPLE :  23 - 4.
            inc quotient      //◄■ DIVISIONS COUNTER.
            jmp divisions
        finish1:
            mov dividend, eax //◄■ LAST DIVIDEND (USED FOR REMAINDER). EXAMPLE : 3.
    }

// ▼ CALCULATE REMAINDER WITH TWO DECIMALS.
    int remainder = 0;
    _asm {
        // ▼ MULTIPLY LAST DIVIDEND BY 100. EXAMPLE : 3 * 100 = 300.
            mov ecx, 100  //◄■ REPEAT 100 TIMES.
            mov eax, 0
        multiply:
            add eax, dividend //◄■ MULTIPLY IS A SERIES OF ADDITIONS. EXAMPLE : EAX + 3.
            loop multiply
        // ▼ GET REMAINDER WITH TWO DECIMALS.
        decimals:
            cmp eax, divisor
            jb  finish2  //◄■ IF DIVIDEND < DIVISOR
            sub eax, divisor  //◄■ DIVIDEND - DIVISOR. EXAMPLE : 300 - 4.
            inc remainder //◄■ DIVISIONS COUNTER.
            jmp decimals
        finish2:
    }
    cout << "The result is " << quotient <<  "." << remainder; //◄■ EXAMPLE : 5.75
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2019-01-21
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多