问题
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.
回答1:
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.
回答2:
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
}
};
回答3:
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 print1.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.
来源:https://stackoverflow.com/questions/9226400/portable-printing-of-exponent-of-a-double-to-c-iostreams