Портативная печать показателя двойки до C ++ iostreams - PullRequest
15 голосов
/ 10 февраля 2012

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

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

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

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

1e-08

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

1e-008

Как можно сделать оба выхода одинаковыми?

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

РЕДАКТИРОВАТЬ: вывод GCC 1e-08, а не 1e-8 (как первоначально было указано), поэтому равен соответствующий.Извините за путаницу.

EDIT2: На самом деле переименован в «мантисса» в «экспоненту» после замечания Дитмара. В Википедии также есть раздел о мантиссе и значимой .

Ответы [ 3 ]

11 голосов
/ 10 февраля 2012

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

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

3 голосов
/ 10 февраля 2012

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

В вашей функции main() может быть создан следующий класс RAII, чтобы обеспечить вам одинаковое поведение 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
    }
};
2 голосов
/ 02 марта 2016

Проблема в том, что 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, чтобы включить соответствующую печать экспоненты. Поведение по умолчанию было изменено на соответствующий стандарту режим печати экспонентов .

См. Прерывание изменений в Visual C ++ 2015 .Для более старых версий см. Ответ @ Мануэля.

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

e, E

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

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

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

...