在Assembly中将正数除以负数

时间:2017-10-04 22:05:07

标签: assembly x86 negative-number integer-division divide-by-zero

我想创建一个可以计算除法问题的程序。问题是,当我尝试除以负数时,我的程序崩溃了,即使我创建了一个名为" DivNeg"应该是为了防止它崩溃。有没有人有关于如何解决这个问题的想法?

这是我的汇编代码

    .386

.model flat

public _Divide

.code

_Divide proc
        mov eax, [esp + 4]  ; First address ; this is the dividend
        mov ebx, [esp + 8]  ; Second address ; this is the divisor

        cmp ebx, 0
        je  DivZero
        cmp ebx, 0
        jnae    DivNeg

        cdq
        idiv ebx            ; To divide by eax by ebx
        mov ebx, [esp + 12] ; Third address; this is the remainder
        jmp Done1

DivZero:
    mov     eax,-1          ; If user divides by zero, this will set the result to negative 1
    mov     edx, 0          ; If user divides by zero, this will set the remainder to 0
    mov     ebx,[esp +12]   ; Needed for the remainder if divided by 0
    cmp     ebx, 0
    je      Done2


Done1:
    mov     [ebx], edx
    je  Done1

DivNeg:
    cmp     ebx, 0
    jge     Done2
    mov     eax, -1
    neg     eax
    je      DivNeg


Done2:
        ret
_Divide endp

        end

2 个答案:

答案 0 :(得分:1)

cdq / idiv ebx只会在两种情况下引发#DE(Divide Exception):

  • eax =任何内容,ebx = 0.除以零。
  • eax = 0x80000000,ebx = -1。这会溢出,因为正确的答案不适合eax。 2的补码有符号表示中的最负数(最高幅度)没有反转。 -2^31符合带符号的32位整数,但+2^31不适合。 (在C中,这就是为什么INT_MIN / -1是未定义的行为。)有关详细信息,请参阅Why does integer division by -1 (negative one) result in FPE?

由于溢出是不可能的,cdq / idiv ebx无法判断正分红和负除数。您正在使用cdqeax签名扩展为edx:eax。 (没有它,64b / 32b => 32b除法很容易溢出结果。)

如果您没有在idiv本身崩溃,那么您有一个不同的错误,应该在调试器中单步执行代码。有关使用GDB或Visual Studio进行调试的提示,请参阅标记wiki的底部。

答案 1 :(得分:1)

mov eax, [esp + 4]  ; First address ; this is the dividend
mov ebx, [esp + 8]  ; Second address ; this is the divisor
...
mov ebx, [esp + 12] ; Third address; this is the remainder

这些评论表明您的功能的参数是地址。这意味着您需要取消引用才能对值本身进行任何操作。你为第三个参数正确地做了这个,但在第一个和第二个参数上失败了!

mov  eax, [esp + 4]  ; First address
mov  eax, [eax]      ; this is the dividend
mov  ebx, [esp + 8]  ; Second address
mov  ebx, [ebx]      ; this is the divisor
cmp ebx, 0
je  DivZero
cmp ebx, 0
jnae    DivNeg

您不需要重复cmp指令。标志保持为第二次条件跳转设置。另外,因为EQ条件被淘汰,最好使用jna,或者更好地使用jl

cmp  ebx, 0
je   DivZero
jl   DivNeg
Done1:
 mov [ebx], edx
 je  Done1

这个代码非常有问题! (无限循环与不必要的掉落) 更好写:

Done1:
  mov  [ebx], edx   ;Return remainder
  jmp  Done2

如果我是你,我会放置 Done1 标签,高出3行,以便始终完成防止空指针的检查。

Done1:
  mov  ebx,[esp +12]   ; Needed for the remainder if divided by 0
  cmp  ebx, 0
  je   Done2
  mov  [ebx], edx
  jmp  Done2

如果红利和分配器均为正值,您可以安全地使用div代替idiv。 当被除数为正且除法器为负时你可以否定除法器,像以前一样使用div,但否定商。

DivNeg:
  neg  ebx
  cdq
  div  ebx
  neg  eax
  jmp  Done1