在汇编中进行一些计算后继续得到 NaN

Keep getting NaN after doing some calculations in assembly

我正在开发一个求解二次方程根的程序。我能够在 root1 子例程中获得第一个根。但是,当我尝试求解 root2 中的第二个根时,二次公式的“/2a”部分始终产生 NaN。

代码如下:

INCLUDE Irvine32.inc    
INCLUDE macros.inc

.data                                                   
a real8 ?
b real8 ?
cc real8 ?

a2 real8 ?
b2 real8 ?
cc2 real8 ?

two real8 2.0
four real8 4.0

two2 real8 2.0
four2 real8 4.0

ten real8 1000.0
num real8 10.0


.code                                   
main PROC       
    finit 
    mWrite "Enter coefficient (a): "
    call ReadFloat
    fst a
    fstp a2

    mWrite "Enter coefficient (b): "
    call ReadFloat
    fst b
    fstp b2

    mWrite "Enter coefficient (c): "
    call ReadFloat
    fst cc
    fstp cc2
    
    mWrite "Roots: "
    call root1
    call Crlf
    call root2

    ;call showfpustack
exit
main ENDP

root1 PROC
    
    ; b^2
    fld b
    fmul b
    fchs    ; flip sign
    fst b
    
    ; 4 * a * c
    fld four
    fmul a
    fmul cc
    fchs
    fsub b 
    fsqrt
    fst four


    fld b
    fchs
    fsqrt
    

    fchs
    fadd four
    fst b

    fld two
    fmul a
    fst two


    fld b
    fdiv two

    call WriteFloat
    call showfpustack

    ret
root1 endp

root2 PROC

    fld b2
    fmul b2
    fchs
    fst b2


    fld four2
    fmul a2
    fmul cc2
    fchs
    fsub b2
    fsqrt
    fst four2


    fld b2
    fchs
    fsqrt


    fchs
    fsub four2
    fst b2

    call Crlf
    call WriteFloat

    fld two2
    fmul a2
    fst two2

    fld b2
    fdiv two2

    call showfpustack
    ret
root2 endp
end main

我能够验证之前的计算结果。我只是遇到了这部分问题。

我不会猜测您为什么会得到 NAN,而是会引导您完成计算二次方程的根所需编写的代码。
您会看到您不需要内存中的任何常量,也不需要输入的多个副本 abc.

重置 FPU 环境

fninit

存储一个有趣的常量,我们将重复使用多次:

fld     a             ; (st0) a
fadd    st0           ; (st0) a + a == 2a

计算 D = b^2 - 4ac

fld     b             ; (st0) b                 (st1) 2a
fmul    st0           ; (st0) b * b == b^2      (st1) 2a

fld     c             ; (st0) c                 (st1) b^2  (st2) 2a
fmul    st2           ; (st0) c * 2a == 2ac     (st1) b^2  (st2) 2a
fadd    st0           ; (st0) 2ac + 2ac == 4ac  (st1) b^2  (st2) 2a

fsubp                 ; (st0) b^2 - 4ac == D    (st1) 2a

这里我们需要测试 D 是否不为负,因为 D<0.

不存在根
ftst                  ; This compares st0 to 0.0 and sets flags C3, C2, and C0
fnstsw  ax            ; Copies those flags to AX
sahf                  ; Copies those flags to EFLAGS
jp      IsUnordered   ; This should not happen
jc      IsNegative    ; This is very possible

只有当 D 不为负时,我们才能取平方根。

fsqrt                 ; (st0) sqrt(D)                   (st1) 2a

求第一个根 R1:

fld     b             ; (st0) b                         (st1) sqrt(D)  (st2) 2a
fchs                  ; (st0) -b                        (st1) sqrt(D)  (st2) 2a
fsub    st1           ; (st0) -b - sqrt(D)              (st1) sqrt(D)  (st2) 2a
fdiv    st2           ; (st0) (-b - sqrt(D)) / 2a == R1 (st1) sqrt(D)  (st2) 2a

求第二个根 R2:

fld     b             ; (st0) b                         (st1) R1  (st2) sqrt(D)  (st3) 2a
fchs                  ; (st0) -b                        (st1) R1  (st2) sqrt(D)  (st3) 2a
fadd    st2           ; (st0) -b - sqrt(D)              (st1) R1  (st2) sqrt(D)  (st3) 2a
fdiv    st3           ; (st0) (-b - sqrt(D)) / 2a == R2 (st1) R1  (st2) sqrt(D)  (st3) 2a

此时FPU寄存器栈只有4个条目:

st3 = 2a
st2 = sqrt(D)
st1 = R1
st0 = R2

请注意,在此代码中的任何地方我都不必将任何内容存储回内存。与 FPU 合作需要仔细规划。重新排列表达式通常是有利的,重新使用以前计算的值总是好的。