...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
locale old_locale; locale tmp_locale(old_locale, new nonfinite_num_put<char>); locale new_locale(tmp_locale, new nonfinite_num_get<char>);
stringstream ss; ss.imbue(new_locale); double inf = numeric_limits<double>::infinity(); ss << inf; // Write out. BOOST_MATH_ASSERT(ss.str() == "inf"); double r; ss >> r; // Read back in. BOOST_MATH_ASSERT(inf == r); // Confirms that the double values really are identical. cout << "infinity output was " << ss.str() << endl; cout << "infinity input was " << r << endl; // But the string representation of r displayed will be the native type // because, when it was constructed, cout had NOT been imbued // with the new locale containing the nonfinite_numput facet. // So the cout output will be "1.#INF on MS platforms // and may be "inf" or other string representation on other platforms.
Note | |
---|---|
From Boost 1.48, lexical_cast no longer uses stringstreams internally, and is now able to handle infinities and NaNs natively on most platforms. |
Without using a new locale that contains the nonfinite facets, previous versions
of lexical_cast
using stringstream
were not portable (and often failed) if nonfinite values are found.
locale old_locale; locale tmp_locale(old_locale, new nonfinite_num_put<char>); locale new_locale(tmp_locale, new nonfinite_num_get<char>);
Although other examples imbue individual streams with the new locale, for the streams constructed inside lexical_cast, it was necessary to assign to a global locale.
locale::global(new_locale);
lexical_cast
then works as
expected, even with infinity and NaNs.
double x = boost::lexical_cast<double>("inf"); assert(x == std::numeric:limits<double>::infinity()); string s = boost::lexical_cast<string>(numeric_limits<double>::infinity()); assert(s == "inf");
Warning | |
---|---|
If you use stringstream inside your functions, you may still need to use a global locale to handle nonfinites correctly. Or you need to imbue your stringstream with suitable get and put facets. |
Warning | |
---|---|
You should be aware that the C++ specification does not explicitly require
that input from decimal digits strings converts with rounding to the nearest
representable floating-point binary value. (In contrast, decimal digits
read by the compiler, for example by an assignment like |
See conversion and rounding for more information on nearest representable and rounding and Exploring Binary for much detail on input and round-tripping difficulties.
Most iostream libraries do in fact achieve the desirable nearest representable floating-point binary value for all values of input. However one popular STL library does not quite achieve this for 64-bit doubles. See Decimal digit string input to double may be 1 bit wrong for the bizarre full details.
If you are expecting to 'round-trip' lexical_cast
or serialization
, for example
archiving and loading, and want to be absolutely certain
that you will always get an exactly identical double value binary pattern,
you should use the suggested 'workaround' below that is believed to work
on all platforms.
You should output using all potentially significant decimal digits, by setting
stream precision to std::numeric_limits<double>::max_digits10
, (or for the appropriate floating-point
type, if not double) and crucially, require scientific
format, not fixed
or automatic (default), for example:
double output_value = any value; std::stringstream s; s << setprecison(std::numeric_limits<double>::max_digits10) << scientific << output_value; s >> input_value;
It is vital that the same locale is used when an archive is saved and when
it is loaded. Otherwise, loading the archive may fail. By default, archives
are saved and loaded with a classic C locale with a boost::archive::codecvt_null
facet added. Normally you do not have to worry about that.
The constructors for the archive classes, as a side-effect, imbue the stream
with such a locale. However, if you want to use the facets nonfinite_num_put
and nonfinite_num_get
with archives, then you have to manage the locale manually. That is done
by calling the archive constructor with the flag boost::archive::no_codecvt
,
thereby ensuring that the archive constructor will not
imbue the stream with a new locale.
The following code shows how to use nonfinite_num_put
with a text_oarchive
.
locale default_locale(locale::classic(), new boost::archive::codecvt_null<char>); locale my_locale(default_locale, new nonfinite_num_put<char>); ofstream ofs("test.txt"); ofs.imbue(my_locale); boost::archive::text_oarchive oa(ofs, no_codecvt); double x = numeric_limits<double>::infinity(); oa & x;
The same method works with nonfinite_num_get
and text_iarchive
.
If you use the nonfinite_num_put
with trap_infinity
and/or
trap_nan
flag with a serialization
archive, then you must set the exception mask of the stream. Serialization
archives do not check the stream state.
nonfinite_facet_simple.cpp give some more simple demonstrations of the difference between using classic C locale and constructing a C99 infinity and NaN compliant locale for input and output.
See nonfinite_facet_sstream.cpp
for this example of use with std::stringstream
s.
For an example of how to enforce the MSVC 'legacy' "1.#INF" and "1.#QNAN" representations of infinity and NaNs, for input and output, see nonfinite_legacy.cpp.
Treatment of signaling NaN is demonstrated at ../../example/nonfinite_signaling_NaN.cpp
Example ../../example/nonfinite_loopback_ok.cpp shows loopback works OK.
Example ../../example/nonfinite_num_facet.cpp shows output and re-input of various finite and nonfinite values.
A simple example of trapping nonfinite output is at nonfinite_num_facet_trap.cpp.
A very basic example of using Boost.Archive is at ../../example/nonfinite_serialization_archives.cpp.
A full demonstration of serialization by Francois Mauger is at ../../example/nonfinite_num_facet_serialization.cpp