为什么printf()会将float类型提升为double类型?

41

来自之前的一个问题

如果您尝试将float传递给printf,则在printf接收到它之前,它将被提升为double

printf()是可变参数函数吗?因此,可变参数函数会在将float参数传递给printf之前将其提升为double吗?


7
是的,通过“默认参数提升”实现。 - Keith Thompson
2
同样地,char 被提升为 int,所以 char c=127; printf("%d", c); 也能正确工作。 - Weather Vane
2
我认为这个引用不完全正确(或者没有包含完整的上下文);当传递给函数时,float值并不总是被转换为double,但是当作为可变参数函数(如printf)的尾随参数传递时,它们会被转换为double - davmac
5
您的引语被错误引用了,或者可能是来自旧版本。在谷歌搜索“C Primer Plus”会出现第五版的PDF,实际的引文是“那是因为 C 自动将类型为 float 的值扩展为类型为 double,当它们作为参数传递给任何函数时(如 printf()),该函数没有明确声明参数类型。”请注意,本翻译力求准确和简洁,不包含其他内容。 - user2357112
1
@LightnessRacesinOrbit:嗯,现在已经太晚了。 - user2357112
显示剩余15条评论
4个回答

35
是的,传递给变参函数的浮点数参数会提升为double类型。 草案C99标准的第6.5.2.2节“函数调用”中写道:
[...]并且类型为float的参数将被提升为double。这些被称为默认参数提升。[...] 草案C++标准的第5.2.2节“函数调用”中写道:
[...]如果是一个需要浮点数提升(4.6)的浮点类型,那么在调用之前,该参数的值将被转换为提升后的类型。[...]
以及第4.6节:
一个float类型的prvalue可以转换为double类型的prvalue。值不变。

cppreference很好地涵盖了C++可变参数函数的default conversions:

  • std::nullptr_t会转换为void*
  • 浮点参数会像浮点数提升一样转换为double
  • bool、char、short和未作用域枚举会像整数提升一样转换为int或更宽的整数类型

我们可以看到在C中,也许在C++中,这种转换是为了与K&R C兼容而保留下来的,来自Rationale for International Standard—Programming Languages—C(我强调):

为了与过去的实践相兼容,在没有原型声明的情况下,所有参数的提升都按照K&R中描述的方式进行,包括不总是理想的将float提升为double


2
是的,可变参数函数中的浮点参数会被提升为双精度。只有在尾部参数中。请修复这个问题。 - 2501

24
关于问题的原因,很简单:C(和C++)标准将double视为“默认”浮点类型。不是float(当我们使用浮点数时,许多程序员会将其作为默认值)。
这可以通过观察以下内容来看到:
  1. 3.14是一个double(如果你想要一个float,你需要额外添加一个f
  2. 标准数学函数默认采用double(例如sin()采用double;如果你想使用float,你需要使用sinf())。
因此,在可变参数函数调用中,似乎更“自然”地将float提升为double,因为double是该语言中的“自然”默认值。

2
许多程序员在使用浮点数时默认使用的不是“float”。我不知道其他人怎么做,但我通常使用“double”。只有在需要存储具有相当高固有误差的大量测量数据的文件时,我才会使用“float”。 - glglgl
3
关于 C 语言,我有一个很大的遗憾是当 long double 被添加进去时,没有包括任何机制来指定可变参数函数是否期望将所有浮点数值转换为最长类型 (long double)、特定类型 double 或者期望 long doubledouble 被传递不同。依我看,如果代码可以说“将所有整数状的东西都转换为这种类型,所有浮点状的东西都转换为那种类型”,那么多年来会节省很多时间和可移植性上的问题。 - supercat

17

给定一个函数原型,当使用尾部参数时,类型为浮点数的自动提升1。函数print使用它们:

int printf(const char * restrict format, ...);

1(摘自:ISO/IEC 9899:201x 6.5.2.2 函数调用)
6. 对每个参数执行整数提升,具有 float 类型的参数被提升为 double。这些称为默认参数提升。
7. 默认参数提升在尾部参数上执行。


12

因为(C99或C11)标准规定了这样做。请参见2501的回答

有几个实用的原因:历史(最早的C实现被用于系统编程,浮点运算并不重要),以及在当前(平板电脑,桌面,服务器...)处理器上,double的算术运算与float相比大约同样有效(但一些廉价的微控制器没有FPU,或者只能通过硬件添加float,并且需要为每个double操作使用库)。最后,我想这样的规则使得稍微简单一些的调用约定ABI

float视为一种类似于short double的东西(当然,在C中这是非法的)。float主要在您需要紧凑存储器(并且可以承受精度损失)时才有用。另请参见http://floating-point-gui.de/获取更多信息。


我不确定是否有时间做那件事。或者等几天再说。 - Basile Starynkevitch
1
因为[...]标准是这样规定的。Kernighan和Ritchie在《C程序设计语言》(第一版,1978年)中也是这样规定的。 - user1818839
1
注意: "char" 和 "short" 会自动扩展并传递为 "int"。 - Giacomo Catenazzi
2
C标准没有这样说。引用要么被错误引述,要么是从一版中引用的,而那一行是错误的。 - user2357112
1
我相信提升仅适用于可变参数函数中的可变参数,例如printf。如果您有一个函数原型采用float类型,则会传递一个float类型。 - Jens
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接