assembly - 将 FASM 中的 80 位 float 与给定精度进行比较

标签 assembly x86 floating-point fasm x87

我正在编写一个程序,在循环中使用 Nilakantha 级数计算 Pi,精度至少为 0.05%。此循环的退出条件应该是当前计算值 res 与先前计算值 prev 拟合 |res - prev| 时<= 0.0005。我已经阅读了 FASM 中的一些浮点比较,但仍然不完全理解它是如何工作的。目前该程序只是无限执行,从不退出循环。在调试过程中,我看到 float 经常变成 1.#IND00,它应该是 NaN。如何写出准确的比较?

format PE console
entry start

include 'win32a.inc'


section '.code' code readable executable
; 3 + 4/(2*3*4) - 4 / (4*5*6) + 4/(6*7*8) - ...
start:
FINIT
piLoop:

; calculating denominator of fraction that will be added: x1*x2*x3
FLD [denominator]
FMUL [zero]
FADD [x1]
FMUL [x2]
FMUL [x3]
FSTP [denominator]

; changing denominator product values for next loop: x1 +=2, x2 += 2, x3 += 2
FLD [x1]
FADD [stepValue]
FSTP [x1]
FLD [x2]
FADD [stepValue]
FSTP [x2]
FLD [x3]
FADD [stepValue]
FSTP [x3]

;calculating numerator: multiplying numerator by -1
FLD [numerator]
FMUL [sign]
FSTP [numerator]

; calculating fraction: +-4 / (x1 * x2 * x3)
FLD [numerator]
FDIV [denominator]
FSTP [fraction]

; adding calculated fraction to our answer
FLD [res]
FADD [fraction]
FSTP [res]

; the comparison part, incorrect?
FLD [res]
FSUB [prev]
FABS
FCOM [accuracy]
FSTSW AX
SAHF

add [i], 1


; prev = res
FLD [res]
FSTP [prev]
jb endMet
jmp piLoop
endMet:

invoke printf, steps_string, [i]

invoke getch
invoke ExitProcess, 0

section '.data' data readable writable
steps_string db "Calculation completed. The Nilakantha Series took %d steps.",10,0
pi_string db "accurate pi = %lf, calculated pi = %lf", 10, 0


res dq 3.0
x1 dq 2.0
x2 dq 3.0
x3 dq 4.0
stepValue dq 2.0
fraction dq 0.0
numerator dq -4.0
denominator dq 0.0
sign dq -1.0
zero dq 0.0
N dd 20
i dd 0
accuracy dq 0.0005
calc dq ?
prev dq 3.0

section '.idata' import data readable
library kernel, 'kernel32.dll',\
        msvcrt, 'msvcrt.dll',\
        user32,'USER32.DLL'

include 'api\user32.inc'
include 'api\kernel32.inc'
import kernel,\
       ExitProcess, 'ExitProcess',\
       HeapCreate,'HeapCreate',\
       HeapAlloc,'HeapAlloc'
include 'api\kernel32.inc'
import msvcrt,\
       printf, 'printf',\
       sprintf, 'sprintf',\
       scanf, 'scanf',\
       getch, '_getch'  

最佳答案

(只是扩展我的评论,以便得到答案。)

背景:用于浮点比较的复杂指令序列源于早期 x86 CPU 没有板载 FPU;它是一个可选的独立芯片,它与 CPU 交互的能力是有限的。所以FCOM指令不能直接设置CPU的FLAGS寄存器。相反,它设置浮点状态字,该状态字位于浮点协处理器内部。 FSTSW指令可用于从协处理器获取状态字并将其加载到通用CPU寄存器中,然后SAHF将获取AH的相应位并将其写入FLAGS。

完成所有这些之后,您最终得到了标志集来指示比较的结果,并且状态字的位被布局以便以与整数比较相同的方式设置标志:ZF将被设置如果数字相等,如果差值严格为负,则为 CF,依此类推。所以你现在可以使用条件跳转,如 ja , jb等,就像无符号整数比较一样。请注意,PF=1 意味着比较是无序的(至少有一个操作数为 NaN),因此您需要首先检查这一点。

(PPro 添加了 FCOMI,它以与 fcom/fstsw/sahf 相同的方式从 FP 中设置 EFLAGS,从而避免了额外的指令。另请参阅 Why do x86 FP compares set CF like unsigned integers, instead of using signed conditions?)


但是,您的代码有 add [i], 1介于两者之间,并且像大多数 x86 算术指令一样,它根据结果设置 FLAGS。因此,您仔细检索的 FLAGS 会被覆盖,并且 jb向下几行基于 add 的结果而不是 FCOM 。因此您需要重新排列它们。

例如,执行 add之前SAHF 。或之前fcomi .

关于assembly - 将 FASM 中的 80 位 float 与给定精度进行比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64495829/

相关文章:

assembly - 如何在程序集引导加载程序中打印按下的键盘按键?

c++ - 为什么C++使用32位寄存器来存储8位值

c - x86 汇编中 "imul"指令的不熟悉语法

c++ - 不同进程数的不同浮点答案

math - float 学有问题吗?

math - float 学有问题吗?

assembly - x86 asm 反汇编程序库

C++/ASM - "Operand size conflict"、 "Improper operand type"

gcc - 让 GCC 优化手工 assembly

c - Linux 内核函数 memblock_alloc_range_nid 未出现在地址空间中