【问题标题】:What precisely does the %g printf specifier mean?%g printf 说明符究竟是什么意思?
【发布时间】:2019-06-07 07:09:43
【问题描述】:

%g 说明符的行为方式似乎与大多数来源记录的行为方式不同。

根据我发现的大多数来源,在使用 printf 说明符的多种语言中,%g 说明符应该等同于 %f%e - 以提供的输出较短的为准价值。例如,在撰写此问题时,cplusplus.com says 表示 g 说明符的含义是:

使用最短的表示:%e%f

PHP manual says 的意思是:

g - %e%f 的较短者。

here's a Stack Overflow answer 声称

%g 使用最短的表示。

a Quora answer 声称:

%g 打印这两种表示中最短的数字

但这种行为并不是我在现实中看到的。如果我编译并运行这个程序(作为 C 或 C++ - 这是一个在两者中具有相同行为的有效程序):

#include <stdio.h>

int main(void) {
    double x = 123456.0;
    printf("%e\n", x);
    printf("%f\n", x);
    printf("%g\n", x);
    printf("\n");

    double y = 1234567.0;
    printf("%e\n", y);
    printf("%f\n", y);
    printf("%g\n", y);
    return 0;
}

...然后我看到这个输出:

1.234560e+05
123456.000000
123456

1.234567e+06
1234567.000000
1.23457e+06

显然,%g 输出与上述xy%e%f 输出不完全匹配。更重要的是,%g 看起来也没有最小化输出长度;如果yx 一样没有以科学记数法打印,那么y 的格式可以更简洁。

我上面引用的所有消息来源都是在骗我吗?

我在支持这些格式说明符的其他语言中看到了相同或相似的行为,这可能是因为它们在底层调用了 printf 系列 C 函数。例如,我在 Python 中看到了这个输出:

>>> print('%g' % 123456.0)
123456
>>> print('%g' % 1234567.0)
1.23457e+06

在 PHP 中:

php > printf('%g', 123456.0);
123456
php > printf('%g', 1234567.0);
1.23457e+6

在 Ruby 中:

irb(main):024:0* printf("%g\n", 123456.0)
123456
=> nil
irb(main):025:0> printf("%g\n", 1234567.0)
1.23457e+06
=> nil

控制这个输出的逻辑是什么?

【问题讨论】:

    标签: c floating-point language-agnostic printf format-specifiers


    【解决方案1】:

    这是 C11 标准中g/G 说明符的完整描述:

    表示浮点数的 double 参数是 转换为 fe 样式(或在 G 的情况下以 FE 样式转换 转换说明符),取决于转换的值和 精确。如果非零则令 P 等于精度,如果精度为 6 省略,如果精度为零,则为 1。然后,如果转换与 样式 E 的指数为 X:

         如果 P > X ≥ -4,则转换为 具有样式 f(或 F)和精度 P - (X + 1)
    否则, 转换使用样式e(或E)和精度P - 1.

    最后,除非 使用 # 标志,从小数部分中删除任何尾随零 结果的一部分和小数点字符被删除,如果 没有剩余的小数部分。

    双重参数 表示无穷大或 NaN 以 fF 的样式转换 转换说明符。

    这种行为有点类似于简单地使用%f%e 中的最短表示,但不是等效的。有两个重要的区别:

    • 使用%g 时,尾随零(可能还有小数点)会被去除,这可能导致%g 说明符的输出与%f%g 的输出不完全匹配%e 会产生的。
    • 关于是使用%f-style 还是%e-style 格式的决定完全基于%e-style 表示法中所需的指数大小,不 直接取决于哪个表示会更短。在多种情况下,此规则会导致 %g 选择更长的表示形式,例如问题中显示的%g 使用科学记数法,即使这会使输出的 4 个字符长于所需的长度。

    如果 C 标准的措辞难以解析,Python documentation 会提供相同行为的另一种描述:

    一般格式。对于给定的精度p &gt;= 1, 这会将数字四舍五入为p 有效数字和 然后将结果格式化为定点格式 或以科学计数法,取决于其大小。

    确切的规则如下:假设 结果格式化为演示类型'e' 和 精度p-1 将具有指数exp。然后 如果-4 &lt;= exp &lt; p,数字被格式化 具有演示类型'f' 和精度 p-1-exp。否则,数字被格式化 具有演示类型'e' 和精度p-1。 在这两种情况下,都会删除无关紧要的尾随零 从有效位开始,小数点也是 如果后面没有剩余数字,则删除。

    正负无穷,正负无穷 零和 nans 的格式分别为 inf-inf0-0nan,无论 精度。

    0 的精度被视为等同于 1 的精度。默认精度为6

    互联网上许多声称%g 只是从%e%f 中挑选最短的消息来源是完全错误的。

    【讨论】:

    • 称它们为“来源”可能会给这些网页带来过多的可信度。
    【解决方案2】:

    我最喜欢的双打格式是“%.15g”。它似乎在每种情况下都做正确的事。我很确定 15 也是双精度的最大可靠十进制精度。

    【讨论】:

    • -1;这没有回答问题。
    • 其他人已经回答了一般问题,所以我认为无需重复。我只是提供一个有用的建议。
    • 64 位浮点值可以准确a length of up to 767 places 表示十进制值。如果您需要不丢失信息的十进制表示,"%.15g" 会做正确的事。如果您的要求是以全精度获得十进制表示,则它不会做正确的事情。
    • 有趣的是,您的示例 0.1000000000000000055511151231257827021181583404541015625 显示 55 个十进制数字。这比 64 位双精度要多得多。每个十进制数字需要大约 3.32 位来指定,因此其中 55 个需要 183 位,更不用说符号和指数的空间了。因此,我不确定您的示例在那里显示的确切内容。我怀疑所有这些额外的数字都没有任何意义。你怎么看?
    猜你喜欢
    • 1970-01-01
    • 2011-03-12
    • 2014-10-29
    • 2016-01-27
    • 2014-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多