Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Input Output

Loopback testing

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.

Comparing with output using Built-in types

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] Note

Do not expect the output from different platforms to be identical, but cpp_dec_float, cpp_bin_float (and other backends) outputs should be correctly rounded to the number of digits requested by the set precision and format.

Macro BOOST_MP_MIN_EXPONENT_DIGITS

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.


PrevUpHomeNext