Что именно означает спецификатор% g printf? - PullRequest
0 голосов
/ 12 января 2019

Спецификатор %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 для x или y выше. Более того, не похоже, что %g также минимизирует длину вывода; y можно было бы отформатировать более кратко, если бы, как и x, в научной нотации было напечатано , а не .

Все ли источники, которые я цитировал выше, лгут мне?

Я вижу идентичное или похожее поведение в других языках, которые поддерживают эти спецификаторы формата, возможно, потому, что они скрытно обращаются к семейству функций printf. Например, я вижу этот вывод в 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

Какая логика управляет этим выводом?

Ответы [ 2 ]

0 голосов
/ 13 января 2019

Мой любимый формат для парных символов: "% .15g". Кажется, в каждом случае поступает правильно. Я почти уверен, что 15 - это максимально надежная десятичная точность в двойном числе.

0 голосов
/ 12 января 2019

Это полное описание спецификатора g / G в стандарте C11:

A double аргумент, представляющий число с плавающей запятой: преобразован в стиле f или e (или в стиле F или E в случае G спецификатор преобразования), в зависимости от преобразованного значения и точность. Пусть P равна точности, если она не равна нулю, 6, если точность равна опущен или 1, если точность равна нулю. Затем, если преобразование с стиль E будет иметь показатель степени X :

если P > X ≥ −4, преобразование со стилем f (или F) и точностью P - (X + 1) .
в противном случае преобразование выполняется со стилем e (или E) и точностью P - 1.

Наконец, если используется флаг # , все завершающие нули удаляются из дробного часть результата и символ десятичной точки удаляются, если дробной части не осталось.

A double аргумент представляющий бесконечность или NaN преобразуется в стиле f или F спецификатор преобразования.

Это поведение несколько похоже на простое использование кратчайшего представления из %f и %e, но не эквивалентно. Есть два важных различия:

  • Конечные нули (и, возможно, десятичная точка) удаляются при использовании %g, что может привести к тому, что выход спецификатора %g не будет точно соответствовать тому, что или %f или %e произвел бы.
  • Решение о том, использовать ли форматирование %f или %e, принимается исключительно на основе размера показателя, который необходим в нотации %e, и не не напрямую зависит от того, какое представление будет короче. Есть несколько сценариев, в которых это правило приводит к %g выбору более длинного представления, как показано в вопросе, где %g использует научную запись, даже если это делает выходные данные на 4 символа длиннее, чем нужно.

В случае, если формулировку стандарта C трудно проанализировать, документация Python предоставляет другое описание того же поведения:

Общий формат. Для заданной точности <code>p >= 1, это округляет число до <code>p значащих цифр и затем форматирует результат в любом формате с фиксированной запятой или в научной записи, в зависимости от его величины.

Точные правила таковы: предположим, что результат отформатирован с типом представления <code>'e' и точность <code>p-1 будет иметь показатель степени <code>exp. затем если <code>-4 <= exp < p, число форматируется с типом представления <code>'f' и точностью <code>p-1-exp. В противном случае номер форматируется с типом представления <code>'e' и точностью <code>p-1. В обоих случаях незначительные завершающие нули удаляются от значения, и десятичная точка также удаляется, если после него нет оставшихся цифр.

Положительная и отрицательная бесконечность, положительная и отрицательная ноль и nans отформатированы как <code>inf, <code>-inf, <code>0, <code>-0 и <code>nan соответственно, независимо от точность.

Точность <code>0 рассматривается как эквивалент точность <code>1. Точность по умолчанию составляет <code>6.

Многие источники в Интернете, утверждающие, что %g просто выбирает самое короткое из %e и %f, просто ошибочны.

...