120 lines
4.9 KiB
Plaintext
120 lines
4.9 KiB
Plaintext
[/
|
|
Copyright 2011 - 2020 John Maddock.
|
|
Copyright 2013 - 2019 Paul A. Bristow.
|
|
Copyright 2013 Christopher Kormanyos.
|
|
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt).
|
|
]
|
|
|
|
[section:input_output Input Output]
|
|
|
|
[h4 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 __fundamental `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/test_cpp_bin_float_io.cpp 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
|
|
[@../../test/string_data.ipp stringdata] test cases in a file.
|
|
|
|
[h4 Comparing with output using __fundamental 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
|
|
[@http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf 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 [*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.]
|
|
|
|
|
|
[h4 Macro BOOST_MP_MIN_EXPONENT_DIGITS]
|
|
|
|
[@http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf C99 Standard]
|
|
for [/e and E] 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
|
|
__fundamental 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.
|
|
|
|
[endsect] [/section:input_output Input Output]
|