首页 > 解决方案 > %g printf 说明符究竟是什么意思?

问题描述

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

printf根据我发现的大多数来源,在使用说明符的多种语言中,说明%g符应该等同于%f%e- 任何一个会为提供的值产生更短的输出。例如,在写这个问题的时候,cplusplus.com 说说明g符的意思是:

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

PHP手册说这意味着:

g - 更短的%e%f

是一个堆栈溢出的答案,声称

%g使用最短的表示。

还有一个 Quora 的回答声称:

%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输出不完全匹配其中一个或以上的%e%f输出。更重要的是,它看起来也不是最小化输出长度;如果它没有以科学记数法打印,则可以更简洁地格式化。xy%gyx

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

我在支持这些格式说明符的其他语言中看到了相同或相似的行为,这可能是因为它们在后台调用了printfC 函数系列。例如,我在 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

在红宝石中:

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

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

标签: cfloating-pointlanguage-agnosticprintfformat-specifiers

解决方案


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

根据转换的值和精度,表示浮点数的f精度参数以样式或e(或以样式FEG 转换说明符的情况下)进行转换。如果不为零,则令P等于精度,如果省略精度,则令 P 等于 6,如果精度为零,则令 P 等于 1。然后,如果带有样式的转换E的指数为X

     如果P > X ≥ −4,则转换为样式f(or F) 和精度P − (X + 1)
     否则,转换为样式e(or E) 和精度P − 1。

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

表示无穷大或 NaN的f参数以orF 转换说明符的样式进行转换。

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

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

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

一般格式。对于给定的精度p >= 1,这会将数字四舍五入为p有效数字,然后根据其大小将结果格式化为定点格式或科学记数法。

精确的规则如下:假设用表示类型'e'和精度格式化的结果p-1将具有 exponent exp。然后如果-4 <= exp < p,数字被格式化为表示类型'f'和精度 p-1-exp。否则,数字将使用presentation type'e'和precision进行格式化p-1。在这两种情况下,从有效数字中删除无关紧要的尾随零,如果小数点后面没有剩余数字,则也将删除小数点。

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

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

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


推荐阅读