I'm trying to print a floating point number in x86_64 assembly but it just prints the value as zero.
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.
This is my program:
# 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)
's %f
format specifier wants a double
.
There is no way to get printf to accept a float
, only double
or long double
.
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.
Thus C provides no way for a caller to pass a float
to printf
, so it has no conversion for it. %f
means double
. %lf
also works for double
in modern printf implementations, C99/C11 and C++11. You can safely use the same %lf
format string with a double
for printf
and scanf
.
Note that scanf
is different. float *
and double *
aren't affected by those promotions, so you can actually scan into a float
with %f
.
CVTSS2SD .num(%rip), %xmm0
If you look at compiler output, you'll see gcc do everything you did. It uses RIP-relative addressing for static storage as usual.
GCC also uses pxor
to 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.
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
.
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.
If you used %g
(scientific notation) or %a
(hex representation of the double
bit-pattern), the non-zero bits would show up. (Unless maybe if you had Denormals Are Zero mode enabled in the MXCSR, although glibc might use purely integer stuff to pick apart FP bit-patterns when converting to base-10 strings; it's a hard problem.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With