[PATCH 1/2] libstdc++: Robustify long double std::to_chars testcase [PR98384]

Patrick Palka ppalka@redhat.com
Mon Feb 22 21:55:58 GMT 2021


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



More information about the Libstdc++ mailing list