问题描述
我正在尝试在 x86_64 程序集中打印一个浮点数,但它只是将值打印为零.
I'm trying to print a floating point number in x86_64 assembly but it just prints the value as zero.
关于这个已经有几个问题了.一个似乎通过确保您在 %al 中设置您使用的向量寄存器的数量来解决.另一个显示您需要有 16 个字节的堆栈对齐.但是,我正在做这两件事,但仍然没有得到正确的输出.
There's a few questions about this already. One appeared to be resolved by ensuring that you set the number of vector registers you're using in %al. Another showed that you need to have a stack alignment of 16 bytes. But, I'm doing both those things and still not getting correct output.
这是我的程序:
# prints a floating point value
.section .rodata
.fmt: .string "num: %f
"
.num: .float 123.4
.section .text
.global main
.type main, @function
main:
subq $8, %rsp # 16-byte alignment
# print my number
movss .num, %xmm0 # load float value
movq $.fmt, %rdi # load format string
movb $1, %al # use 1 vector register
call printf
# exit
addq $8, %rsp # undo alignment
movq $0, %rax # return 0
ret
推荐答案
printf(3)
的 %f
格式说明符需要 double
.没有办法让 printf 接受 float
,只有 double
或 long double
.
printf(3)
's %f
format specifier wants a double
. There is no way to get printf to accept a float
, only double
or long double
.
C 的默认参数提升 指定对诸如 foo(char *fmt, ...)
等可变参数函数的调用将 float
提升为 double
,并将窄整数类型的通常整数提升为 int
,用于匹配原型的 ...
部分的尾随参数.(这同样适用于调用没有原型的函数的所有参数.)N1570 6.5.2.2 函数调用,第 6 和 7 小节.
C's default argument promotions specify that calls to variadic functions like foo(char *fmt, ...)
promote float
to double
, and perform the usual integer promotions of narrow integer types to int
, for trailing args that match the ...
part of the prototype. (The same applies to all args for calling functions with no prototype.) N1570 6.5.2.2 Function calls, subsections 6 and 7.
因此 C 没有为调用者提供将 float
传递给 printf
的方法,因此它没有对其进行转换,并且 %f
表示 double
.(%lf
也适用于 double
,假设实现忽略非整数/wchar_t
转换.接受 %lf
for double
在 C99/C11 和C++11 printf
实现,因此您可以安全地使用相同的 %lf
格式字符串和 double
用于 printf
和 scanf
).
Thus C provides no way for a caller to pass a float
to printf
, so it has no conversion for it, and %f
means double
. (%lf
also works for double
, assuming the implementation ignores it for non-integer / wchar_t
conversions. Accepting %lf
for double
is required in C99/C11 and C++11 printf
implementations, so you can safely use the same %lf
format string with a double
for printf
and scanf
).
请注意,scanf
是不同的,因为 float *
和 double *
不受这些促销的影响.
Note that scanf
is different, because float *
and double *
aren't affected by those promotions.
如果你看看 编译器输出,你会看到 gcc 做你所做的一切,并且 pxor
首先将寄存器归零以打破对旧值的错误依赖%xmm0
.(cvtss2sd
糟糕的设计让目标不变.)gcc 错误在谨慎的一面,并在许多情况下插入异或归零指令以打破错误的依赖关系.
If you look at compiler output, you'll see gcc do everything you did, and pxor
-zero the register first to break the false dependency on the old value of %xmm0
. (cvtss2sd
's poor design leaves the upper 64 bits of the destination unchanged.) gcc errs on the side of caution, and inserts xor-zeroing instructions to break false dependencies in many cases.
您可能得到 0,因为 xmm0 的高位恰好为零.当 printf
将 xmm0 的低 64 位视为 double
(x86 上的 IEEE binary64),它在尾数的低 32 位中找到 123.4f
的位模式,其余为零.作为一个 64 位 double
,这个位模式代表一个非常小的(次正规)数,所以它与 %f
一起显示为零.
You're probably getting 0 because the upper bits of xmm0 happen to be zero. When printf
looks at the low 64 bits of xmm0 as a double
(IEEE binary64 on x86), it finds the bit pattern for 123.4f
in the low 32 bits of the mantissa, and the rest zero. As a 64-bit double
, this bit-pattern represents a very small (subnormal) number, so it comes out as zero with %f
.
您可以尝试使用 float
的等效项(例如在 http://www.h-schmidt.net/FloatConverter/IEEE754.html),在低半部分设置一些位,看看你得到了什么.
You can try the equivalent with a float
, (e.g. on http://www.h-schmidt.net/FloatConverter/IEEE754.html), setting some bits in the low half to see what you get.
如果您使用了 %g
(科学记数法)或 %a
(double
位模式的十六进制表示),则非零位会出现.(除非您在 MXCSR 中启用了非正规数为零模式.)
If you used %g
(scientific notation) or %a
(hex representation of the double
bit-pattern), the non-zero bits would show up. (Unless you had Denormals Are Zero mode enabled in the MXCSR.)
这篇关于如何使用 printf 打印单精度浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!