printf %f 如何处理 32 位浮点数

How does printf %f work on a 32-bit float

%f printf 格式代码被指定为对类型 double [source] 的值进行操作。然而,一个简单的测试程序表明它也可以与 float 类型的值一起使用。这是如何工作的?

整数类型的等效情况(例如intlong long int)"works"因为在小端机器上,32位整数的低位字节恰好重叠 64 位整数的低位字节,因此只要高位为 0,您就会得到 "right" 答案。

但是 floatdouble 不可能是这种情况,因为浮点格式不能像这样互换。如果不进行(相当复杂的)转换为其他格式,您根本无法将浮点值打印为双精度值。尝试通过类型双关来做到这一点只会打印垃圾。

最重要的是,printf 是可变的。编译器不一定在编译时知道将使用什么格式说明符,只知道参数的类型。因此,我唯一可以推测的是,传递给可变参数函数的 all float 值将无条件地升级为 double。但令我感到困惑的是,我可能已经用 C 编程了这么久,却不知道这一点。

C 在这里如何进行隐式强制转换?

来源:

#include <stdio.h>
#include <math.h>

int main() {
  float x[2] = {M_PI, 0.0};
  printf("value of x: %.16e\n", x[0]);
  printf("size of x: %lu\n", sizeof(x[0]));

  double *xp = (double *)&x[0];
  printf("value of *xp: %.16e\n", *xp);
  printf("size of *xp: %lu\n", sizeof(*xp));

  double y = M_PI;
  printf("value of y: %.16e\n", y);
  printf("size of y: %lu\n", sizeof(y));

  int i[2] = {1234, 0};
  printf("value of i: %lld\n", i[0]);
  printf("sizeof of i: %lu\n", sizeof(i[0]));

  long long *ip = (long long *)&i[0];
  printf("value of i: %lld\n", *ip);
  printf("sizeof of i: %lu\n", sizeof(*ip));

  return 0;
}

输出:

value of x: 3.1415927410125732e+00
size of x: 4
value of *xp: 5.3286462644388174e-315
size of *xp: 8
value of y: 3.1415926535897931e+00
size of y: 8
value of i: 1234
sizeof of i: 4
value of i: 1234
sizeof of i: 8

编译命令和版本:

$ gcc test_float.c -o test_float
$ gcc --version
gcc (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Therefore the only thing I can surmise is that all float values passed to a variadic function would be upgraded to double, unconditionally.

是的 - 完全正确。

来自C标准;

6.5.2.2.7 The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

而"default argument promotions"规则会将float提升为double,相关部分为:

6.5.2.2.6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double.

参见Variadic arguments and Default argument promotions

At the function call, each argument that is a part of the variable argument list undergoes special implicit conversions known as default argument promotions.

.

Each argument of integer type undergoes integer promotion (see below), and each argument of type float is implicitly converted to the type double.