本文介绍了如何使用printf打印单精度浮点数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在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,仅接受doublelong 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表示printfscanf).

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打印单精度浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 10:57