Переносная печать экспоненты двойника на 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 ответа:
Нет никакого манипулятора, контролирующего форматирование экспоненты (я предполагаю, что вы имеете в виду экспоненту, а не мантиссу; кроме того, "официальное" имя, используемое для мантиссы, является значимым). Что еще хуже,я не вижу никакого правила в стандарте 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
и т. д.