【问题标题】:Correct format specifier for double in printfprintf 中 double 的正确格式说明符
【发布时间】:2011-05-14 22:17:36
【问题描述】:

printf 中double 的正确格式说明符是什么?是%f 还是%lf?我相信是%f,但我不确定。

代码示例

#include <stdio.h>

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

【问题讨论】:

  • 如果你被 C89 库困住,"%lf" 是未定义的;在 C99 和 C11 库中,它被定义为与 "%f" 相同。
  • 您的变体与以往一样正确。 %lfdouble 的正确格式说明符。但它在 C99 中变得如此。在此之前必须使用%f

标签: c floating-point printf double format-specifiers


【解决方案1】:

"%f" 是(或至少一个)正确的双精度格式。 没有float 没有格式,因为如果您尝试将float 传递给printf,它会在printf 收到它之前被提升为double 1。在当前标准下,"%lf" 也是可以接受的——如果后面跟着 f 转换说明符(等等),l 被指定为无效。

请注意,这是printf 格式字符串与scanf(和fscanf 等)格式字符串有很大不同的地方。对于输出,您将传递一个 ,当作为可变参数传递时,该值将从 float 提升到 double。对于输入,您传递了一个未提升的指针,因此您必须告诉scanf 您是要读取float 还是double,所以对于scanf%f 表示您想阅读 float%lf 表示您想阅读 double (并且,对于它的价值,对于 long double,您使用 %Lfprintfscanf)。


1. C99,§6.5.2.2/6:“如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并且具有浮点类型的参数提升为双精度。这些被称为默认参数提升。”在 C++ 中,措辞有些不同(例如,它不使用“原型”一词),但效果是一样的:所有可变参数在被函数接收之前都经过默认提升。

【讨论】:

  • 请注意g++ 在使用-Wall -Werror -pedantic 编译时会拒绝%lferror: ISO C++ does not support the ‘%lf’ gnu_printf format
  • @kynan:如果是这样(至少假设当前版本的 g++),那是 g++ 中的一个错误。对于 C89/90 和 C++98/03,允许 l 是一个扩展。 C99/11 和 C++11 标准要求实现允许它。
  • 奇怪的是,scanf 确实想要doubles 由%lf 表示:它抱怨说它期望float * 并发现double * 只是%f .
  • @JerryCoffin g++ 仍然默认为 g++98 模式
  • @EricDand 那是因为scanf 将指针指向存储读取内容的位置,因此需要知道指向的空间有多大,而printf取值本身,“默认参数提升”意味着两者都以doubles 结尾,所以l 本质上是可选的。
【解决方案2】:

鉴于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。 与上一版相比的主要变化包括:

  • %lf printf 中允许的转换说明符

7.19.6.1 fprintf 函数

7长度修饰符及其含义为:

l (ell) 指定 (...) 对后面的 a、A、e、E、f、F、g 或 G 转换说明符没有影响。

L 指定后面的 a、A、e、E、f、F、g 或 G 转换说明符适用于 long double 参数。

fprintf 指定的相同规则适用于printfsprintf 和类似功能。

7.19.6.2 fscanf 函数

11长度修饰符及其含义为:

l (ell) 指定 (...) 后面的 a、A、e、E、f、F、g 或 G 转换说明符适用于类型指针指向的参数双;

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 -> 双重
  • %Lf -> 长双。

对于fscanf,它是:

  • %f -> 浮动
  • %lf -> 双重
  • %Lf -> 长双。

【讨论】:

    【解决方案3】:

    它可以是%f%g%e,具体取决于您希望数字的格式。有关详细信息,请参阅herel 修饰符在 scanfdouble 中是必需的,但在 printf 中不需要。

    【讨论】:

    • -1:l(小写)修饰符用于整数类型(cplusplus.com/reference/clibrary/cstdio/printf),L 用于浮点类型。此外,L 修饰符需要long double,而不是普通的double
    • user470379:那我的回答哪里矛盾了?我不是说过printf 中不需要l 对于double
    【解决方案4】:

    %lf 格式是 double 的完全正确的printf 格式,与您使用的完全一样。您的代码没有任何问题。

    旧的(C99 之前)版本的 C 语言不支持 printf 中的 %lf 格式,这在 printfscanf 中的 double 格式说明符之间造成了表面上的“不一致”。这种表面上的不一致已在 C99 中得到修复。

    您不需要在printf 中使用%lfdouble。如果您愿意,也可以使用%f%lf%fprintf 中是等效的)。但在现代 C 中,更喜欢将%ffloat 一起使用,%lfdouble%Lflong double 一起使用是非常有意义的,在printfscanf 中始终如此。

    【讨论】:

    • scanf()"%f""%lf" 匹配float *, double *,而不是最后一行所暗示的float, double
    【解决方案5】:

    %Lf(注意大写L)是format specifier 对应long doubles

    对于普通的doubles%e%E%f%g%G 都可以。

    【讨论】:

    • %g%G有什么区别?
    • @yanpas,指数符号的小写/大写。
    • 对不起,%g 和 %G 输出 E 符号。他们还在不同的情况下输出INF和inf
    猜你喜欢
    • 2013-03-14
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多