Переносная печать экспоненты двойных в С++ iostreams
Я хочу напечатать двойное значение до std::cout
portably (GCC, clang, MSVС++), так что вывод будет одинаковым на всех платформах.
У меня проблема с форматированием экспоненты.
Следующая программа
#include <iostream>
int main()
{
std::cout << 0.1e-7 << std::endl;
return 0;
}
Имеет этот вывод с GCC:
1e-08
и следующий вывод с MSVC
1e-008
Как я могу сделать оба выхода одинаковыми?
Извините, если это глупый вопрос, но пока я не нашел ответа.
Кажется, что все форматирование развивается вокруг форматирования всего до мантиссы...
EDIT: вывод GCC 1e-08
not 1e-8
(как изначально указано), поэтому он соответствует. Извините за путаницу.
EDIT2: Фактически переименовали "мантисса" в "экспоненту" после комментария Дитмара. Также есть раздел о Википедии о мантиссе против значительного.
Ответы
Ответ 1
Нет манипулятора, контролирующего форматирование экспоненты (я предполагаю, что вы имеете в виду экспонента, а не мантисса, а также "официальное" имя, используемое для мантиссы, является значительным). Что еще хуже, я не вижу никакого правила в стандарте C, которое ограничивает форматирование экспоненты. Я понимаю, что речь идет о С++, но для целей форматирования стандарт С++ относится к стандарту C.
Единственный подход, о котором я знаю, - использовать собственный фасет std::num_put<char>
, который форматирует значения по желанию. Затем этот грань будет помещен в std::locale
, который, в свою очередь, imbue()
ed в std::cout
. Потенциальная реализация может использовать фасет по умолчанию std::num_put<char>
(или snprintf()
, который, к сожалению, вероятно, проще) для форматирования числа с плавающей запятой, а затем отделяет ведущие нули от экспоненты.
Ответ 2
В то время как ответ Dietmar - это чистый и, вероятно, только действительно переносимый ответ, я случайно нашел быстрый и грязный ответ:
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
}
};
Ответ 3
Проблема в том, что Visual С++ не следовала стандарту C99. В Visual С++ 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 С++ 2015. Для более старых версий см. Ответ @Manuel.
FYI, в C99 standard, мы можем прочитать:
е, е
Двойной аргумент, представляющий число с плавающей запятой, преобразуется в стиле [-] d.ddd e (+ -) dd, где есть одна цифра (которая отлична от нуля, если аргумент отличен от нуля) до десятичной точки символ и количество цифр после того, как оно равно точности; если точность отсутствует, она принимается равной 6; если точность равна нулю, а флаг # не указан, символ десятичной точки не отображается. Значение округляется до соответствующего количества цифр. Спецификатор преобразования E производит число с E вместо e, вводящего экспонента. Показатель всегда содержит не менее двух цифр и только столько цифр, сколько необходимо для представления экспоненты. Если значение равно нулю, показатель равен нулю. Двойной аргумент, представляющий бесконечность или NaN, преобразуется в стиле спецификатора преобразования f или F.
Это разница по сравнению с C90, которая не давала никаких указаний относительно требуемой длины экспоненты.
Обратите внимание, что последние изменения Visual С++ также касаются того, как печатать nan
, inf
и т.д.