首页 > 解决方案 > 无法连续两次从变量中输出协处理器浮点数

问题描述

下午好!在这个例子中,我只是用逗号添加两个数字,将变量保存在tbyte中并在屏幕上连续两次显示相同的变量,但第一次得到11.1,应该是这样,第二次得到 4.667261 E-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

标签: assemblyx86masmmasm32x87

解决方案


你为什么要把fstp qword( double) 变成 tbyte ( long double)?

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

但是您的格式字符串仅告诉 sprintf 查找double8 个字节宽的 args。由于您只将 qword 存储double到 的低 8 个字节resultcrt_sprintf因此可以正确地将第一个可变参数 arg 读取为double. (x86 是 little-endian,因此低 8 个字节位于 sprintf 正在查看的堆栈地址。)

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

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

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

如果sizeof(long double)只有 8,那么它double与您不能使用该 C 库 printf x87 tbyte 值相同。(除非它有一些非标准的扩展。) 在这种情况下,您只需更改result为也是 a qword,匹配您正在做的商店。


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

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


推荐阅读