问题描述
我正在尝试在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\n"
.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
,以进行尾随匹配原型...
部分的args. (这同样适用于所有不带原型的调用函数的arg.) 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
格式字符串与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
的较差设计使目标地址的高64位保持不变.)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
(IEEE binary64在x86上),它在尾数的低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打印单精度浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!