如何使用 printf 打印单精度浮点数
How to print a single-precision float with printf
我试图在 x86_64 程序集中打印一个浮点数,但它只是将值打印为零。
已经有一些关于此的问题。一个似乎是通过确保 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 来解决的。但是,我正在做这两件事,但仍然没有得到正确的输出。
这是我的程序:
# 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 , %rsp # 16-byte alignment
# print my number
movss .num, %xmm0 # load float value
movq $.fmt, %rdi # load format string
movb , %al # use 1 vector register
call printf
# exit
addq , %rsp # undo alignment
movq [=10=], %rax # return 0
ret
printf(3)
's %f
format specifier wants a double
.
没有办法让 printf 接受 float
,只有 double
或 long double
.
C 的默认参数提升 指定对可变参数函数的调用,如 foo(char *fmt, ...)
将 float
提升为 double
,并执行通常的整数提升窄整数类型到 int
,用于匹配原型的 ...
部分的尾随参数。 (这同样适用于调用没有原型的函数的所有参数。)N1570 6.5.2.2 Function calls, subsections 6 and 7.
因此,C 无法让调用者将 float
传递给 printf
,因此它没有任何转换。 %f
表示double
。 %lf
也适用于 double
in modern printf implementations、C99/C11 和 C++11。您可以安全地将相同的 %lf
格式字符串与 double
用于 printf
和 scanf
.
注意scanf
是不同的。 float *
和 double *
不受这些促销的影响,因此您实际上可以使用 %f
.
扫描到 float
加载CVTSS2SD .num(%rip), %xmm0
如果你照常看compiler output, you'll see gcc do everything you did. It uses 。
GCC 还使用 pxor
首先将寄存器归零,以打破对 %xmm0
旧值的错误依赖。 (cvtss2sd
's poor design leaves the upper 64 bits of the destination unchanged.) ,并插入异或归零指令以在许多情况下打破错误的依赖关系。
您可能得到 0,因为 xmm0 的高位恰好为零。当printf
将xmm0的低64位看成double
(IEEE binary64 on x86)时,它在尾数的低32位中找到123.4f
的位模式,其余为零。作为 64 位 double
,此位模式表示一个非常小的(次正规)数字,因此它在 %f
.
中显示为零
您可以尝试使用 float
进行等效操作(例如在 http://www.h-schmidt.net/FloatConverter/IEEE754.html 上),在低半部分设置一些位以查看结果。
如果您使用 %g
(科学计数法)或 %a
(double
位模式的十六进制表示),将显示非零位。 (除非你在 MXCSR 中启用了 Denormals Are Zero 模式,尽管 glibc 在转换为 base-10 字符串时可能会使用纯整数来分离 FP 位模式;这是一个难题。)
我试图在 x86_64 程序集中打印一个浮点数,但它只是将值打印为零。
已经有一些关于此的问题。一个似乎是通过确保 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 来解决的。但是,我正在做这两件事,但仍然没有得到正确的输出。
这是我的程序:
# 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 , %rsp # 16-byte alignment
# print my number
movss .num, %xmm0 # load float value
movq $.fmt, %rdi # load format string
movb , %al # use 1 vector register
call printf
# exit
addq , %rsp # undo alignment
movq [=10=], %rax # return 0
ret
printf(3)
's %f
format specifier wants a double
.
没有办法让 printf 接受 float
,只有 double
或 long double
.
C 的默认参数提升 指定对可变参数函数的调用,如 foo(char *fmt, ...)
将 float
提升为 double
,并执行通常的整数提升窄整数类型到 int
,用于匹配原型的 ...
部分的尾随参数。 (这同样适用于调用没有原型的函数的所有参数。)N1570 6.5.2.2 Function calls, subsections 6 and 7.
因此,C 无法让调用者将 float
传递给 printf
,因此它没有任何转换。 %f
表示double
。 %lf
也适用于 double
in modern printf implementations、C99/C11 和 C++11。您可以安全地将相同的 %lf
格式字符串与 double
用于 printf
和 scanf
.
注意scanf
是不同的。 float *
和 double *
不受这些促销的影响,因此您实际上可以使用 %f
.
float
加载CVTSS2SD .num(%rip), %xmm0
如果你照常看compiler output, you'll see gcc do everything you did. It uses
GCC 还使用 pxor
首先将寄存器归零,以打破对 %xmm0
旧值的错误依赖。 (cvtss2sd
's poor design leaves the upper 64 bits of the destination unchanged.)
您可能得到 0,因为 xmm0 的高位恰好为零。当printf
将xmm0的低64位看成double
(IEEE binary64 on x86)时,它在尾数的低32位中找到123.4f
的位模式,其余为零。作为 64 位 double
,此位模式表示一个非常小的(次正规)数字,因此它在 %f
.
您可以尝试使用 float
进行等效操作(例如在 http://www.h-schmidt.net/FloatConverter/IEEE754.html 上),在低半部分设置一些位以查看结果。
如果您使用 %g
(科学计数法)或 %a
(double
位模式的十六进制表示),将显示非零位。 (除非你在 MXCSR 中启用了 Denormals Are Zero 模式,尽管 glibc 在转换为 base-10 字符串时可能会使用纯整数来分离 FP 位模式;这是一个难题。)