无法连续两次从变量输出协处理器浮点数

Can't output coprocessor float from variable two times in a row

下午好!在这个例子中,我简单地用逗号添加两个数字,将变量保存在 tbyte 中,并在屏幕上连续两次显示相同的变量,但是第一次我得到 11.1,理所当然,第二次4.667261E-062。为什么会这样?

还有一个问题,在 tbyte 中是否有可能以某种方式按数组类型保存和访问数字?例如,将数字存储在 dd 中,我可以以 4 为增量保存和读取它们,例如 result [0]result [4] 等。是否可能与 tbyte 一起使用,如何使用?如果我理解正确的话——应该是 10.

的一步
.386
.model flat,stdcall
option casemap:none

include \masm32\include\masm32rt.inc

.data
titletext db  'Title',0
frmt db 'Result1 = %.7G',10
     db 'Result2 = %.7G',0
buff db 1024 dup (?)
result tbyte ?
num1 qword 5.5
num2 qword 5.6

.code
start:
    finit
    fld qword ptr [num1]
    fld qword ptr [num2]
    fadd
    fstp qword ptr [result]

    invoke crt_sprintf,addr buff,addr frmt, result, result
    invoke MessageBox,0,addr buff,addr titletext,MB_OK
    invoke ExitProcess,0
end start

为什么要将 fstp qword (double) 转换成 1 tbyte (long double)?

哦,那可能是你的错误。据推测,invoke 宏为每个 result 宏参数推送 12 个字节。 (因为 10 字节 tbyte 填充到 4 字节栈槽的倍数是 12)。

但是您的格式字符串只告诉 sprintf 查找 double 8 字节宽的参数。由于您只将 qword double 存储到 result 的低 8 字节,crt_sprintf 可以正确读取第一个可变参数作为 double。 (x86 是小端字节序,因此低 8 字节位于 sprintf 正在查看的堆栈地址。)

但是第二次 %G 转换将在前一个 arg 结束后立即寻找另一个 double。根据格式字符串,它应该在 8 个字节之后。 但是您的 invoke 实际推送的内容与此不匹配。所以第二个 %G 读取与两个 12 字节推送重叠的 8 个字节。

高4字节(包括指数和符号位)可能是0,只有尾数的低31位非零,给你一个非常小的次正规数。您可以使用调试器将内存检查为 double 并查看它代表 sprintf 读取的值。

如果 long double在那个C库中是10字节的x87类型,用%LG,用fstp tbyte .

如果 sizeof(long double) 仅为 8,则它与 double 相同,并且您无法使用该 C 库打印 x87 tbyte 值。 (除非它有一些非标准的扩展名。)在那种情况下,您只需将 result 也更改为 qword,以匹配您正在做的商店。


此外,您没有平衡 x87 堆栈;使用 faddp 所以它在 fstp 之后是空的。 (如果您的汇编程序需要操作数,请使用 faddp st(1)st1,但它喜欢拼写 x87 寄存器名称。)

您在 x87 堆栈非空的情况下进行函数调用在技术上违反了调用约定,但显然 crt_sprintf 没有使用 st0..7 的全部 8 个,因此它不会从 x87 堆栈溢出中获取 NaN。