assembly - 在汇编中进行一些计算后继续获取 NaN
问题描述
我正在开发一个求解二次方程根的程序。我能够在 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,而是会引导您完成计算二次方程的根所需编写的代码。
您会看到内存中不需要任何常量,也不需要输入a、b和c的多个副本。
重置 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 合作需要仔细规划。重新排列表达式通常是有利的,并且重新使用先前计算的值总是一种胜利。
推荐阅读
- javascript - 单独导入 Vuetify 组件的正确方法是什么?
- python - 基于接受命令行参数的单独自定义映像创建 docker 映像
- r - 使“周”功能双周
- angular - 从 Angular 调用休息服务后读取属性时出错
- python - 如何将鼠标单击绑定到 Tkinter 中所有窗口中的函数
- vim - 在 vim 脚本中执行的 shell 命令的输出可以重定向到文件吗?
- android - android:显示来自服务的对话框
- node.js - 在 Node.js 的 typescript 类成员函数中访问响应对象
- angular - formgroupname 在 ngfor 中具有相同的索引
- python - 将标准差函数转换为 numpy 形式