【问题标题】:Incorrect output when computing square root with Nasm x86 Assembly使用 Nasm x86 程序集计算平方根时输出不正确
【发布时间】:2013-12-05 06:26:41
【问题描述】:

首先,我要感谢所有阅读本文并试图帮助我解决这个问题的人,非常感谢。

我正在编写一个汇编程序,以使用二分法计算 IEEE-754 格式的数字的平方根。我相信我对二分法的实现是正确的(尽管我可能可以更有效地做到这一点),因为当我使用我的实现打印出数字的平方根并打印出 FPU 给我的平方根时fsqrt 在这两种情况下,对于我尝试过的每个输入数字,我都会得到相同的结果。所以这让我相信我打印的数字不正确。我究竟做错了什么?

Here is the code: 

EXTERN printf 
EXTERN sscanf 
GLOBAL main

SEGMENT .data
n:      DD 0                                    ; Storage for float 
n_sqrt: DD 0 
l_bound: DD 0 
u_bound: DD 0 
epsilon: DD 0x3727C5AC                          ; Error bound. IEEE 754 representation of 0.00001
midpoint: DD 0 
format: DB "%f", 0 
form:   DB "%s", 10, 0
formh:  DB "%f", 10, 0 
outFormat: DB "The square root of %lf is: ", 0 
fsqrtForm: DB "fsqrt(n) = %f", 10, 0  

SEGMENT .text 
main: 
        push    ebp                             ; compose stack frame 
        mov     ebp, esp                        
        mov     eax, [ebp + 12]                 ; eax = address of param table


    finit                                   ; initialize FPU stack

    pushad                                  ; preserve all registers before making a system call
    push    n                               ; store f.p. number in n 
    push    format                          ; format at f.p. 
    push    dword [eax+4]                   ; push the first command line parameter 
    call    sscanf                          ; convert it to f.p., store it in n 
    add     esp, 4*3
    popad 

    fld     dword [n]                       ; st0 = n 
    fld1                                    ; st0 = 1; st1 = n 
    fadd    st1                             ; st0 = n+1; st1 = n
    fst     dword [u_bound]                 ; u_bound = n+1; st0 = n+1 ; st1 = n
    fld1                                    ; st0 = 1; st1 = n+1; st2 = n 
    fadd    st0                             ; st0 = 2; st1 = n+1; st2 = n 
    fdivr   st1                             ; st0 = (n+1)/2; st1 = n+1; st2 = n
    fstp    dword [midpoint]                ; midpoint = (n+1)/2; st0 = n+1; st1 = n 
    fcompp                                  ; clear st0 and st1 
.L1:    
        fld     dword [n]                       ; st0 = n
        fld     dword [midpoint]                ; st0 = midpoint; st1 = n
        fmul    st0                             ; st0 = midpoint*midpoint; st1 = n  
        fcomip  st1                             ; midpoint*midpoint < n ? ; st0 = n
        jae     .L2                             ; NO   
        fstp    st0                             ; clear st0
        fld     dword [midpoint]                ; st0 = midpoint
        fstp    dword [l_bound]                 ; l_bound = midpoint; clear st0 
        jmp     .L3                             ; continue 
.L2:                                            ; Else 
        fstp    st0                             ; clear st0 
        fld     dword [midpoint]                ; st0 = midpoint
        fstp    dword [u_bound]                 ; u_bound = midpoint 
.L3:                                            ; midpoint = (l_bound + u_bound)/2.0  
        fld     dword [u_bound]                 ; st0 = u_bound
        fld     dword [l_bound]                 ; st0 = l_bound; st1 = u_bound
        faddp   st1, st0                        ; st0 = l_bound + u_bound
        fld1                                    ; st0 = 1; st1 = l_bound + u_bound  
        fadd    st0                             ; st0 = 2; st1 = l_bound + u_bound
        fdivrp  st1, st0                        ; st0 = (l_bound + u_bound)/2.0
        fstp    dword [midpoint]                ; midpoint = (l_bound + u_bound)/2.0 ; clear st0 


    fld     dword [epsilon]                 ; st0 = epsilon
    fld     dword [u_bound]                 ; st0 = u_bound; st1 = epsilon
    fld     dword [l_bound]                 ; st0 = l_bound; st1 = u_bound; st2 = epsilon
    fsubp   st1, st0                        ; st0 = u_bound - l_bound; st1 = epsilon 
    fcomip  st1                             ; check: is u_bound - l_bound > epsilon? st0 = epsilon
    ja      .L5                             ; YES break while loop
    fstp    st0                             ; NO - clear st0 and continue
    jmp     .L1                             
.L5:

    jmp     .printSqrt 
.end:


    pop     ebp 
    ret 

;---------------------------------------------------------------------------------------------------        
.printSqrt:

    fld     dword [n]
    sub     esp, 8 
    fstp    qword [esp]
    push    outFormat
    call    printf 
    add     esp, 12 

    fld     dword [midpoint]
    sub     esp, 8 
    fstp    qword [esp]
    push    formh
    call    printf 
    add     esp, 12 

    fld     dword [n]
    fsqrt 
    sub     esp, 8
    fstp    dword [esp]
    push    fsqrtForm
    call    printf 
    add     esp, 12


    jmp     .end 
    ret 

[另外:输入的数字作为命令行参数传递。]

再次感谢!

【问题讨论】:

  • 不确定我是否理解您所说的:它打印不正确,但在第二段中,您说它“打印”与 fsqrt 相同。你的意思是当你调试时,你看到 number 计算正确,但打印不正确?
  • 哦,我没有我的程序集参考,但我很确定你使用 %gf 打印四边形,而不是 %f。
  • 我从来没有看到它计算正确,我所看到的只是当我从我的方法中打印出结果时,它与我从fsqrt 中打印出结果时相同。我认为'long float'是 %lf 。我会试试的。谢谢!

标签: assembly x86 nasm square-root bisection


【解决方案1】:

你从esp 中减去 8,但只在那里存储一个 dword。试试fstp qword [esp]

【讨论】:

  • 谢谢!将该行更改为 fstp qword [esp] 更正了 fsqrt 的输出。
【解决方案2】:

我发现我有三个小问题:

1) 正如弗兰克所说,当我保留 8 个字节时,我只在 esp 中存储了一个双字,而不是 qword

2)上线:fdivrp st1, st0 ; st0 = (l_bound + u_bound)/2.0我把divrp改成了divp。拥有divrp 实际上计算2.0/(l_bound + u_bound),这是我想要的倒数。

3) 我需要将行 ja .L5 更改为 jb .L5。当l_bound - u_bound 的差异大于错误界限epsilon 时,ja 会中断循环,而我希望在差异小于错误时终止循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 2011-12-13
    • 2015-09-20
    • 1970-01-01
    • 2015-09-22
    • 2020-06-06
    相关资源
    最近更新 更多