printf中double类型的正确格式说明符是什么?

589

在 printf 中,double 的正确格式说明符是什么?是 %f 还是 %lf?我认为应该是 %f,但我不确定。

代码示例

#include <stdio.h>

int main()
{
   double d = 1.4;
   printf("%lf", d); // Is this wrong?
}

22
如果你使用的是 C89 库,那么 "%lf" 是未定义的;在 C99 和 C11 库中,它被定义为与 "%f" 相同。 - pmg
1
你的变量是正确的,%lfdouble 的正确格式说明符。但这是在 C99 中才成为标准。在此之前,必须使用 %f - AnT stands with Russia
5个回答

787
"%f" 是双精度浮点数的(至少之一)正确格式。并不存在浮点数的格式,因为如果你尝试将浮点数传递给 printf,它会在 printf 接收它之前被提升为双精度浮点数1。根据当前标准,"%lf" 也是可以接受的。如果后面跟着 f 转换说明符(以及其他一些),则指定的 l 不起作用。
需要注意的是,这是 printf 格式字符串与 scanf (以及 fscanf 等)格式字符串有很大不同的地方之一。对于输出,您传递的是一个 ,当作为可变参数传递时,该值将从 float 升级为双精度浮点数。对于输入,您传递的是一个 指针,它不会被提升,因此必须告诉 scanf 您要读取一个 float 还是一个 double。因此,对于 scanf%f 表示您要读取一个 float%lf 表示您要读取一个 double(值得一提的是,对于 long double,无论是用于 printf 还是 scanf,都使用 %Lf)。

1. C99,§6.5.2.2/6:“如果表示被调用函数的表达式具有不包括原型的类型,则执行整数提升,并将类型为 float 的参数提升为 double。这些被称为默认参数提升。”在 C++ 中,措辞有些不同(例如,它不使用“原型”一词),但效果相同:所有可变参数在被函数接收之前都会进行默认提升。


12
请注意,在使用-Wall -Werror -pedantic编译时,g++会拒绝使用%lferror: ISO C++ does not support the ‘%lf’ gnu_printf format - kynan
3
如果是这样的话(至少假设使用当前版本的g++),那么这是g++中的一个错误。在C89/90和C++98/03中,允许使用l是一种扩展。C99/11和C++11标准要求实现必须允许其使用。 - Jerry Coffin
1
有趣的是,scanf确实希望用%lf表示double类型:如果只用%f,它会抱怨它期望得到float *而不是double * - Eric Dand
1
@g++仍然默认为g++98模式。 - M.M
9
这是因为scanf需要指向存储读取内容的位置的指针,因此必须知道被指向的空间有多大;而printf则直接取值本身,并且“默认参数提升”意味着它们都成为了double,所以l实际上是可选的。 - TripeHound
显示剩余2条评论

66
根据C99标准(即N1256草案),规则取决于函数类型:fprintf(printf,sprintf等)或scanf。
以下是相关部分的摘录:

前言

本次修订版取代了第一版 ISO/IEC 9899:1990,并由 ISO/IEC 9899/COR1:1994、ISO/IEC 9899/AMD1:1995 和 ISO/IEC 9899/COR2:1996 进行修正。与上一版相比,主要变化包括:

  • printf 中允许使用 %lf 转换说明符。

7.19.6.1 函数 fprintf

7 长度修饰符及其含义如下:

l(小写字母 L)表示对于 a、A、e、E、f、F、g 或 G 转换说明符的后续处理没有影响。

L 表示对于 a、A、e、E、f、F、g 或 G 转换说明符的后续处理应用于 long double 类型的参数。

对于 printfsprintf 及类似函数,适用与 fprintf 相同的规则。

7.19.6.2 函数 fscanf

11 长度修饰符及其含义如下:

l (小写字母 ell) 指定 (...) 后续的 a、A、e、E、f、F、g 或 G 转换说明符适用于类型为指向 double 的参数;

L 指定 (...) 后续的 a、A、e、E、f、F、g 或 G 转换说明符适用于类型为指向 long double 的参数。

12 转换说明符及其含义如下: a、e、f、g 匹配可选带符号的浮点数,(...)

14 转换说明符 A、E、F、G 和 X 也是有效的,并分别与 a、e、f、g 和 x 表现相同。

简而言之,对于 fprintf,以下说明符和对应类型已指定:

  • %f -> double
  • %Lf -> long double。

对于fscanf来说,它的格式如下:

  • %f -> 浮点型
  • %lf -> 双精度浮点型
  • %Lf -> 长双精度浮点型。

37

根据希望数字格式化的方式,它可以是%f%g%e。更多细节请见此处。在使用double时,scanf需要使用l修饰符,但在printf中则不需要。


1
-1: l(小写)修饰符用于整数类型(http://www.cplusplus.com/reference/clibrary/cstdio/printf/),而`L`用于浮点类型。此外,`L`修饰符期望一个`long double,而不是普通的double`。 - user470379
12
那么我的回答和你的回答有什么矛盾之处吗?我没有说在使用 printf 打印 double 类型时需要使用 l - vitaut
对于 printf 函数,浮点类型的长度修饰符 l 会被忽略,因为浮点参数总是会被提升为 double 类型。Jerry Coffin 的回答包含更多信息。 - Hari

21

%lf 是一个完全正确的 printf 格式,可以用于 double 类型,就像您使用它一样。您的代码没有任何问题。

%lf 在旧版(C99 之前)的 C 语言中不支持,在 printf 中为 double 指定格式说明符和在 scanf 中的表现形式之间表面上存在 "不一致性"。这种表面上的不一致性已经在 C99 中得到了修复。

printf 中,您不需要使用 %lf 来表示 double。如果您愿意,也可以使用 %f%lf%fprintf 中是等价的)。但在现代 C 中,最好在 printfscanf 中都一致地使用 %f 来表示 float%lf 来表示 double%Lf 来表示 long double


使用 scanf() 函数时,"%f""%lf" 分别匹配 float *double *,而不是最后一行所暗示的 floatdouble - chux - Reinstate Monica

13

“%g”和“%G”的区别是什么? - yanpas
@yanpas,小写/大写表示指数符号。 - Frédéric Hamidi
抱歉,%g和%G会输出E符号。此外,它们在不同情况下输出INF和inf。 - yanpas

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