15

I want to print a double value to std::cout portably (GCC, clang, MSVC++) such that the output is the same on all platforms.

I have a problem with the formatting of the exponent. The following program

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

Has this output with GCC:

1e-08

and the following output with MSVC

1e-008

How can I make both outputs the same?

I'm sorry if this is a dumb question but I have not found an answer so far. All formatting seems to evolve around the formatting of everything before the mantissa...

EDIT: The output of GCC is 1e-08 not 1e-8 (as originally stated) so it is conforming. Sorry for the confusion.

EDIT2: Actually renamed "mantissa" to "exponent" following Dietmar's remark. There also is a section on Wikipedia on mantissa vs. significant.

Manuel
  • 6,173
  • 6
  • 32
  • 52
  • Have you looked at [manipulators](http://www.cplusplus.com/reference/iostream/manipulators/)? – razlebe Feb 10 '12 at 10:40
  • 1
    @razlebe: I could not find an answer in using manipulators. – Manuel Feb 10 '12 at 12:16
  • I find GCC inconsistent because it prints `1.e-08` and `1.e-18` (two digits) and yet it prints `1.e-256` (three digits). I couldn't find a stream library that solves this (I tried with iostream of course and Boost.Format). So if one wants to have fixed width doubles one needs to reserve and extra space for the possible third digit of the exponent. – alfC Feb 24 '17 at 04:30

3 Answers3

11

There is no manipulator controlling the formatting of the exponent (I assume you mean the exponent rather than the mantissa; also, the "official" name used for the mantissa is significant). To make matters worse, I can't see any rule in the C standard which restricts the formatting of the exponent. I realize that this is about C++ but for the purpose of the formatting details, the C++ standard refers to the C standard.

The only approach I'm aware of is to use an own std::num_put<char> facet which formats the values as desired. This facet would then be put into a std::locale which in turn is imbue()ed into std::cout. A potential implementation could use the default std::num_put<char> facet (or snprintf() which is, unfortunately, probably simpler) to format the floating point number and then strip leading zeros from the exponent.

Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356
  • 4
    The C++ formatting is defined in terms of `printf`, which says that "The exponent shall always contain at least two digits." (So g++ is not conform.) I'm reading from the Posix standard here, which is supposed to be conform with the C standard. But I do have vague memories of text saying that it can't be more than two characters unless necessary (which would make VC wrong as well); I remember having discussed this a long, long time ago, and it was established that VC was non-conformant (which doesn't solve the problem if that's the compiler you have to use). – James Kanze Feb 10 '12 at 10:58
  • 1
    @JamesKanze is it possible to read it as though they meant "character" instead of digit in which case the minus sign would count? – Flexo Feb 10 '12 at 11:03
  • I only had a quick look at the C99 standard (I don't have a newer version) and I didn't find any formatting rules for the exponent. In any case, whatever is conforming or non-conforming, there seems to be some freedom of implementation and there is definitely variation in the implementation. I still think that the method described above is probably the easiest approach to transparently change the produced formatting. – Dietmar Kühl Feb 10 '12 at 11:04
  • @awoodland I don't think so. But this is the Posix standard, not the C standard, and the wording might be slightly different. – James Kanze Feb 10 '12 at 11:11
  • @DietmarKühl I've nothing newer than the C90 standard, and that's at home. Given how long ago it was when I looked at this, it's quite possible that C90 was the latest C standard. I've still got a paper copy at home; I'll check it (if I remember). Not that it helps the OP: as you say, implementations do vary, and we have to live with it. And easiest is relative, although using `std::num_put` to format into a string and fixing that up isn't that difficult (although it's more verbose than I would like). – James Kanze Feb 10 '12 at 11:18
  • @JamesKanze: Sorry for the confusion regarding g++ conformance: g++'s output actually is `1e-08`. – Manuel Feb 10 '12 at 11:38
  • 1
    C90 (in the description of fprintf, %eE) "The exponent always contains at least two digits.", C99 "The exponent always contains at least two digits, and only as many more digits as necessary to represent the exponent." and in C11 (well n1548, I haven't found an affordable edition of C11) has the same wording as C99. – AProgrammer Feb 10 '12 at 13:03
  • 1
    @AProgrammer The C90 sounds like what I remember (and it looks like the version I read in the Posix standard corresponds to C90). The C++ standard (at least through C++03) uses the C90 standard as its basis, so I guess VC++ is conform (and VC makes no pretension of being conform with any C standard). – James Kanze Feb 10 '12 at 13:14
3

While Dietmar's answer is the clean and probably only really portable answer, I accidentally found a quick-and-dirty answer: MSVC provides the _set_output_format function which you can use to switch to "print exponent as two digits".

The following RAII class can be instantiated in your main() function to give you the same behaviour of GCC, CLANG and 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
    }
};
Manuel
  • 6,173
  • 6
  • 32
  • 52
  • This requires at least MSVCR80.dll which is painful to setup to work with MinGW. – Manuel Mar 03 '12 at 18:10
  • Note: as stated in the documentation, `This function is obsolete. Beginning in Visual Studio 2015, it is not available in the CRT.` – BenC Mar 02 '16 at 01:08
2

The problem is that Visual C++ was not following the C99 standard. In Visual C++ 2015, _set_output_format was removed since the compiler now follows the standard:

The %e and %E format specifiers format a floating point number as a decimal mantissa and exponent. The %g and %G format specifiers also format numbers in this form in some cases. In previous versions, the CRT would always generate strings with three-digit exponents. For example, printf("%e\n", 1.0) would print 1.000000e+000. This was incorrect: C requires that if the exponent is representable using only one or two digits, then only two digits are to be printed.

In Visual Studio 2005 a global conformance switch was added: _set_output_format. A program could call this function with the argument _TWO_DIGIT_EXPONENT, to enable conforming exponent printing. The default behavior has been changed to the standards-conforming exponent printing mode.

See Breaking Changes in Visual C++ 2015. For older versions, see @Manuel's answer.

FYI, in the C99 standard, we can read:

e,E

A double argument representing a floating-point number is converted in the style [-]d.ddd e(+-)dd, where there is one digit (which is nonzero if the argument is nonzero) before the decimal-point character and the number of digits after it is equal to the precision; if the precision is missing, it is taken as 6; if the precision is zero and the # flag is not specified, no decimal-point character appears. The value is rounded to the appropriate number of digits. The E conversion specifier produces a number with E instead of e introducing the exponent. The exponent always contains at least two digits, and only as many more digits as necessary to represent the exponent. If the value is zero, the exponent is zero. A double argument representing an infinity or NaN is converted in the style of an f or F conversion specifier.

This is a difference compared to C90 that did not give any indication concerning the required exponent length.

Note that the recent Visual C++ changes also concern how to print nan, inf etc.

Community
  • 1
  • 1
BenC
  • 8,089
  • 3
  • 43
  • 64