gpt4 book ai didi

assembly - 在给定精度下比较 FASM 中的 80 位 float

转载 作者:行者123 更新时间:2023-12-04 08:42:24 24 4
gpt4 key购买 nike

我正在编写一个程序,该程序在循环中使用 Nilakantha 系列计算 Pi,精度至少为 0.05%。这个循环的退出条件应该是当前计算的值 res 和之前计算的值 prev 匹配时 |res - prev| <= 0.0005。我已经阅读了 FASM 中的一些浮点比较,但仍然不完全理解它是如何工作的。目前程序只是无限执行,永远不会退出循环。在调试过程中,我看到浮点数经常变成 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。
完成所有这些之后,您最终获得了 FLAGS 设置以指示比较的结果,并且状态字的位被布局以便以与整数比较相同的方式设置 FLAGS:如果数字是相等,如果差值严格为负,则为 CF,依此类推。所以你现在可以使用条件跳转,比如 ja , jb等,就像无符号整数比较一样。请注意,PF=1 表示比较是无序的(至少一个操作数是 NaN),因此您需要先检查一下。
(PPro 添加了 FCOMI,它从 FP 比较设置 EFLAGS 的方式与 fcom/fstsw/sahf 相同,避免了额外的指令。另见 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com