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

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

3 ответов


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

единственный подход, о котором я знаю это использовать собственный std::num_put<char> фасет, который форматирует значения по желанию. Затем эта грань будет помещена в std::locale который в свою очередь imbue()едь в 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 спецификаторы формата форматируют число с плавающей запятой в виде десятичной мантиссы и экспоненты. The %g и спецификаторы формата и числа в этой форме в некоторых случаях. В предыдущих версиях CRT всегда генерировал строки с трехзначными показателями. Например, printf("%e\n", 1.0)печати 1.000000e+000. это было неправильно: C требует, чтобы если экспонента представляется с использованием только одной или двух цифр, то только две цифры должны быть напечатаны.

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

посмотреть нарушение изменений в Visual C++ 2015. Для более старых версий см. ответ @Manuel.

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

e, E

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

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

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