如何使用 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,只有 doublelong 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 用于 printfscanf.

注意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(科学计数法)或 %adouble 位模式的十六进制表示),将显示非零位。 (除非你在 MXCSR 中启用了 Denormals Are Zero 模式,尽管 glibc 在转换为 base-10 字符串时可能会使用纯整数来分离 FP 位模式;这是一个难题。)