汇编语言程序找到GCD - 浮点异常

时间:2012-11-10 21:54:48

标签: c assembly floating-point greatest-common-divisor

通过这个程序,我打算找到两个数字的GCD。但我得到的结果是“浮点异常(核心转储)”。问题是什么? 我想要生成的代码是

int main() {
int sml, lrg, rem;
read %d sml
read %d lrg
while (sml > 0){
rem = lrg % sml;
lrg = sml;
sml = rem;
}
print %d lrg;
return 0;
}

我生成的程序集文件是:

    .file "gcd.c"
    .section .rodata
.LC0:
    .string "%d"
.LC1:
    .string "%d\n"

    .text
    .globl main
    .type main, @function
main:
    pushl %ebp
    movl  %esp, %ebp
    andl  $-16, %esp
    subl  $32, %esp

    leal  -8(%ebp), %eax    #scan a value
    movl  %eax,  4(%esp)
    movl  $.LC0,  (%esp)
    call scanf

    leal  -12(%ebp), %eax   #scan a value
    movl  %eax,  4(%esp)
    movl  $.LC0,  (%esp)
    call scanf


.L2:
    movl $0, %eax
    cmpl -8(%ebp),%eax
    jle .L0
    jmp .L1

.L0:
    movl  -12(%ebp),%eax
    movl -8(%ebp),%ecx
    movl %eax,%edx
    sarl $31, %edx
    idivl %ecx
    movl %edx,%eax
    movl %eax, -16(%ebp)
    movl -8(%ebp),%edx
    movl %edx, -12(%ebp)
    movl -16(%ebp),%edx
    movl %edx, -8(%ebp)
    jmp .L2

.L1:
    movl -12(%ebp), %eax
    movl  %eax,  4(%esp)
    movl $.LC0, (%esp)
    call printf

    movl $0, %edx

    movl $0, %eax       #end of program
    leave
    ret

.LFE2:
    .size     main, .-main 
    .ident     "GCC: (GNU) 4.2.3 (4.2.3-6mnb1)" 
    .section    .note.GNU-stack,"",@progbits

另一方面,这个汇编代码可以工作

    .file   "check.c"
    .section    .rodata
.LC0:
    .string "%d"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $32, %esp
    leal    20(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    scanf
    leal    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    scanf
    jmp .L2
.L3:
    movl    24(%esp), %eax
    movl    20(%esp), %ecx
    movl    %eax, %edx
    sarl    $31, %edx
    idivl   %ecx
    movl    %edx, 28(%esp)
    movl    20(%esp), %eax
    movl    %eax, 24(%esp)
    movl    28(%esp), %eax
    movl    %eax, 20(%esp)
.L2:
    movl    20(%esp), %eax
    testl   %eax, %eax
    jg  .L3
    movl    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

2 个答案:

答案 0 :(得分:3)

你可能在这一行中除以零:

idivl %ecx

0注册中的ecx值。

答案 1 :(得分:2)

你的比较和跳跃是错误的。工作代码有:

testl   %eax, %eax
jg  .L3

破碎的代码有:

movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1

前者将%eax(包含最近计算的残差)与零进行比较,如果残差为正(大于零),它将继续循环(跳转到.L3)。

后者比较0到-8(%ebp)(最近计算的残差)。请注意,订单不同;它将0到-8(%ebp),而不是-8(%ebp)与0进行比较。testl指令将值(在执行AND之后)与零进行比较。如果该值为正,则“大于”零。 cmpl指令将其第二个操作数与其第一个操作数进行比较;如果第二个操作数超过第一个,则结果为“大于”。这是因为,在英特尔的手册和汇编语言中,指令以相反的顺序写入其操作数。例如,将3变为%eax将是“mov%eax,$ 3”。但是,您使用的汇编程序会撤消英特尔订单中的所有操作数(由于遗留原因)。

因此,如果0小于或等于残差,则断开的代码将继续循环(跳转到.L0)。因此,如果残差为零,则循环继续。您可以将jle更改为jl

jl .L0

或者,您可以消除多余的无条件跳转:

movl $0, %eax
cmpl -8(%ebp),%eax
jge .L1

此外,您可能想要更改:

movl $.LC0, (%esp)
call _printf

为:

movl $.LC1, (%esp)
call _printf

这样您就可以将"%d\n"传递给printf,而不是传递"%d"

顺便说一下,idivl指令会产生除法错误,而不是浮点异常。您的系统误报了错误。