This makes the hexadecimal section of the long double std::to_chars testcase more robust by avoiding false-negative FAILs due to printf using a different leading hex digit than us, and by additionally verifying the correctness of the hexadecimal form via round-tripping through std::from_chars.
Tested on x86, x86_64, powerpc64be, powerpc64le and aarch64. Does this look OK for trunk? libstdc++-v3/ChangeLog: PR libstdc++/98384 * testsuite/20_util/to_chars/long_double.cc: Include <optional>. (test01): Simplify verifying the nearby values by using a 2-iteration loop and a dedicated output buffer to check that the nearby values are different. Factor out the printf-based verification into a local function, and check that the leading hex digits agree before comparing with the output of printf. Also verify the output by round-tripping it through from_chars. --- .../testsuite/20_util/to_chars/long_double.cc | 73 ++++++++++++------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc index 4f72cb65400..34cc03034cc 100644 --- a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc +++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc @@ -26,6 +26,7 @@ #include <cmath> #include <cstring> #include <iterator> +#include <optional> #include <limits> #include <testsuite_hooks.h> @@ -50,6 +51,38 @@ namespace detail void test01() { + // Verifies correctness of the hexadecimal form [BEGIN,END) for VALUE by + // round-tripping it through from_chars (if available). + auto verify_via_from_chars = [] (char *begin, char *end, long double value) { +#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE + long double roundtrip; + auto result = from_chars(begin, end, roundtrip, chars_format::hex); + VERIFY( result.ec == errc{} ); + VERIFY( result.ptr == end ); + VERIFY( roundtrip == value ); +#endif + }; + + // Verifies correctness of the null-terminated hexadecimal form at BEGIN + // for VALUE and PRECISION by comparing it with the output of printf's %La + // conversion specifier. + auto verify_via_printf = [] (char *begin, long double value, + optional<int> precision = nullopt) { + char printf_buffer[1024] = {}; + if (precision.has_value()) + sprintf(printf_buffer, "%.*La", precision.value(), value); + else + sprintf(printf_buffer, "%La", value); + + // Only compare with the output of printf if the leading hex digits agree. + // If the leading hex digit of our form doesn't agree with that of printf, + // then the two forms may still be equivalent (e.g. 1.1p+0 vs 8.8p-3), so we + // don't want a FAIL in this case. But if the leading hex digits do agree, + // then we do expect the two forms to be the same. + if (printf_buffer[strlen("0x")] == begin[0]) + VERIFY( !strcmp(begin, printf_buffer+strlen("0x")) ); + }; + const long double hex_testcases[] = { detail::nextdownl(numeric_limits<long double>::max()), detail::nextupl(numeric_limits<long double>::min()), @@ -92,38 +125,27 @@ test01() if (testcase == 0.0L || isinf(testcase)) continue; - char to_chars_buffer[1024], printf_buffer[1024]; - memset(to_chars_buffer, '\0', sizeof(to_chars_buffer)); - memset(printf_buffer, '\0', sizeof(printf_buffer)); - + char to_chars_buffer[1024] = {}; auto result = to_chars(begin(to_chars_buffer), end(to_chars_buffer), testcase, chars_format::hex); VERIFY( result.ec == errc{} ); *result.ptr = '\0'; - sprintf(printf_buffer, "%La", testcase); - VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) ); + verify_via_from_chars(begin(to_chars_buffer), result.ptr, testcase); + verify_via_printf(to_chars_buffer, testcase); + // Verify the nearby values, and also check they have a different + // shortest form. + for (long double nearby + : { detail::nextdownl(testcase), detail::nextupl(testcase) }) { - // Verify that the nearby values have a different shortest form. - testcase = detail::nextdownl(testcase); - result = to_chars(begin(to_chars_buffer), end(to_chars_buffer), - testcase, chars_format::hex); - VERIFY( result.ec == errc{} ); - *result.ptr = '\0'; - VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0); - sprintf(printf_buffer, "%La", testcase); - VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) ); - - testcase = detail::nextupl(detail::nextupl(testcase)); - result = to_chars(begin(to_chars_buffer), end(to_chars_buffer), - testcase, chars_format::hex); + char nearby_buffer[1024] = {}; + result = to_chars(begin(nearby_buffer), end(nearby_buffer), + nearby, chars_format::hex); VERIFY( result.ec == errc{} ); *result.ptr = '\0'; - VERIFY( strcmp(to_chars_buffer, printf_buffer+strlen("0x")) != 0); - sprintf(printf_buffer, "%La", testcase); - VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) ); - - testcase = detail::nextdownl(testcase); + VERIFY( strcmp(nearby_buffer, to_chars_buffer) != 0); + verify_via_from_chars(begin(nearby_buffer), result.ptr, nearby); + verify_via_printf(nearby_buffer, nearby); } for (int precision = -1; precision < 50; precision++) @@ -132,8 +154,7 @@ test01() testcase, chars_format::hex, precision); VERIFY( result.ec == errc{} ); *result.ptr = '\0'; - sprintf(printf_buffer, "%.*La", precision, testcase); - VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) ); + verify_via_printf(to_chars_buffer, testcase, precision); } } } -- 2.30.1.559.g2283e0e9af