Переносная печать экспоненты двойника на C++ iostreams


Я хочу вывести двойное значение в std::cout переносимо (GCC, clang, MSVC++) таким образом, чтобы выходные данные были одинаковыми на всех платформах.

У меня есть проблема с форматированием экспоненты. Следующая программа

#include <iostream>
int main()
{
    std::cout << 0.1e-7 << std::endl;
    return 0;
}

Имеет этот выход с GCC:

1e-08

И следующий вывод с MSVC

1e-008

Как я могу сделать оба вывода одинаковыми?

Извините, если это глупый вопрос, но я до сих пор не нашел ответа. Все форматирование, кажется, развивается вокруг форматирование всего, что было до мантиссы...

EDIT: выход GCC 1e-08 Не 1e-8 (как было первоначально заявлено), поэтому он соответствует. Извините за путаницу.

EDIT2: фактически переименовал "мантиссу" в "экспоненту" после замечания Дитмара. в Википедии также есть раздел, посвященный мантиссе против значимого.

3 15

3 ответа:

Нет никакого манипулятора, контролирующего форматирование экспоненты (я предполагаю, что вы имеете в виду экспоненту, а не мантиссу; кроме того, "официальное" имя, используемое для мантиссы, является значимым). Что еще хуже,я не вижу никакого правила в стандарте C, которое ограничивает форматирование экспоненты. Я понимаю,что речь идет о C++, но для деталей форматирования стандарт C++ ссылается на стандарт C.

Единственный подход, который мне известен, - это использование собственного std::num_put<char> фасет, который форматирует значения по желанию. Затем эта грань будет помещена в std::locale, который в свою очередь является imbue() ed в std::cout. Потенциальная реализация может использовать фасет по умолчанию std::num_put<char> (или snprintf(), что, к сожалению, возможно проще)для форматирования числа с плавающей запятой, а затем удалить ведущие нули из экспоненты.

В то время как ответ Дитмара-чистый и, вероятно, единственный действительно портативный ответ, я случайно нашел быстрый и грязный ответ: MSVC обеспечивает _set_output_format функция, с помощью которой можно переключиться на "вывести показатель степени в виде двух цифр".

Следующий класс RAII может быть создан в вашей функции main(), чтобы дать вам то же самое поведение GCC, CLANG и MSVC.

class ScientificNotationExponentOutputNormalizer
{
public:
    unsigned _oldExponentFormat;

    ScientificNotationExponentOutputNormalizer() : _oldExponentFormat(0)
    {
#ifdef _MSC_VER
        // Set scientific format to print two places.
        unsigned _oldExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT);
#endif
    }

    ~ScientificNotationExponentOutputNormalizer()
    {
#ifdef _MSC_VER
        // Enable old exponent format.
        _set_output_format(_oldExponentFormat);
#endif
    }
};

Проблема в том, что Visual C++ не следовал стандарту C99. В Visual C++ 2015 _set_output_format был удален, так как компилятор теперь следует стандарту:

Спецификаторы %e и %E форматируют число с плавающей запятой в виде десятичной мантиссы и экспоненты. Спецификаторы формата %g и %G в некоторых случаях также форматируют числа в этой форме. В предыдущих версиях CRT всегда генерировал строки с трехзначными экспонентами. Например, printf("%e\n", 1.0)напечатает 1.000000e+000. это было неверно: C требует, чтобы если экспонента представима только одной или двумя цифрами, то только две цифры должны быть напечатаны.

В Visual Studio 2005 был добавлен глобальный переключатель соответствия: _set_output_format. Программа может вызвать эту функцию с аргументом _TWO_DIGIT_EXPONENT, чтобы включить соответствующую печать экспоненты. поведение по умолчанию было изменено на соответствующий стандартам режим печати экспонент .

См. разрушающие изменения в визуальном C++ 2015 . Для более старых версий, см. ответ @Мануэля.

К вашему сведению, в стандарте C99 мы можем прочитать:

E, E

Двойной аргумент, представляющий собой число с плавающей запятой, преобразуется в стиле [-] D.ddd e (+- ) dd, где перед символом десятичной запятой находится одна цифра (которая не равна нулю, если аргумент не равен нулю), а число цифр после нее равно точности; если точность отсутствует, то она принимается равной 6; если точность равна нулю, то она равна нулю. ноль и флаг # не указаны, символ десятичной запятой не отображается. Значение округляется до соответствующего количества цифр. Электронная спецификатор преобразования производит ряд С Е вместо е введение экспонента. Экспонента всегда содержит по крайней мере две цифры, и только столько цифр, сколько необходимо для представления экспоненты. Если значение равно нулю, то экспонента равна нулю. Двойной аргумент, представляющий бесконечность или NaN, преобразуется в стиле преобразования f или F описатель.

Это различие по сравнению с C90, которое не давало никаких указаний относительно требуемой длины экспоненты.

. отметим, что последние Visual С++ изменения также касаются способов печати nan, inf и т. д.