...one of the most highly
regarded and expertly designed C++ library projects in the
world.

— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards

*Loopback* or *round-tripping* refers
to writing out a value as a decimal digit string using `std::iostream`

,
usually to a `std::stringstream`

, and then reading the string
back in to another value, and confirming that the two values are identical.
A trivial example using `float`

is:

float write; // Value to round-trip. std::stringstream ss; // Read and write std::stringstream. ss.precision(std::numeric_limits<T>::max_digits10); // Ensure all potentially significant bits are output. ss.flags(std::ios_base::fmtflags(std::ios_base::scientific)); // Use scientific format. ss << write; // Output to string. float read; // Expected. ss >> read; // Read decimal digits string from stringstream. BOOST_CHECK_EQUAL(write, read); // Should be the same.

and this can be run in a loop for all possible values of a 32-bit float.
For other floating-point types `T`

,
including built-in `double`

, it
takes far too long to test all values, so a reasonable test strategy is to
use a large number of random values.

T write; std::stringstream ss; ss.precision(std::numeric_limits<T>::max_digits10); // Ensure all potentially significant bits are output. ss.flags(f); // Changed from default iostream format flags if desired. ss << write; // Output to stringstream. T read; ss >> read; // Get read using operator>> from stringstream. BOOST_CHECK_EQUAL(read, write); read = static_cast<T>(ss.str()); // Get read by converting from decimal digits string representation of write. BOOST_CHECK_EQUAL(read, write); read = static_cast<T>(write.str(0, f)); // Get read using format specified when written. BOOST_CHECK_EQUAL(read, write);

The test at test_cpp_bin_float_io.cpp
allows any floating-point type to be *round_tripped* using
a wide range of fairly random values. It also includes tests compared a collection
of stringdata test cases
in a file.

One can make some comparisons with the output of

<number<cpp_bin_float<53, digit_count_2> >

which has the same number of significant bits (53) as 64-bit double precision floating-point.

However, although most outputs are identical, there are differences on some platforms caused by the implementation-dependent behaviours allowed by the C99 specification C99 ISO/IEC 9899:TC2, incorporated by C++.

"For e, E, f, F, g, and G conversions, if the number of significant decimal digits is at most DECIMAL_DIG, then the result should be correctly rounded. If the number of significant decimal digits is more than DECIMAL_DIG but the source value is exactly representable with DECIMAL_DIG digits, then the result should be an exact representation with trailing zeros. Otherwise, the source value is bounded by two adjacent decimal strings L < U, both having DECIMAL_DIG significant digits; the value of the resultant decimal string D should satisfy L<= D <= U, with the extra stipulation that the error should have a correct sign for the current rounding direction."

So not only is correct rounding for the full number of digits not required,
but even if the **optional** recommended practice
is followed, then the value of these last few digits is unspecified as long
as the value is within certain bounds.

Note | |
---|---|

Do not expect the output from different platforms to be |

C99 Standard for format specifiers, 7.19.6 Formatted input/output functions requires:

"The exponent always contains at least two digits, and only as many more digits as necessary to represent the exponent."

So to conform to the C99 standard (incorporated by C++)

#define BOOST_MP_MIN_EXPONENT_DIGITS 2

Confusingly, Microsoft (and MinGW) do not conform to this standard and provide
**at least three digits**, for example `1e+001`

. So if you want the output to match
that from built-in floating-point types on compilers that use Microsofts
runtime then use:

#define BOOST_MP_MIN_EXPONENT_DIGITS 3

Also useful to get the minimum exponent field width is

#define BOOST_MP_MIN_EXPONENT_DIGITS 1

producing a compact output like `2e+4`

,
useful when conserving space is important.

Larger values are also supported, for example, value 4 for `2e+0004`

which may be useful to ensure that
columns line up.