无法连续两次从变量输出协处理器浮点数
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。
下午好!在这个例子中,我简单地用逗号添加两个数字,将变量保存在 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。