[PATCH] libstdc++: Gracefully disable floating-point to_chars on unsupported targets

Patrick Palka ppalka@redhat.com
Mon Dec 21 16:58:38 GMT 2020


This patch conditionally disables the floating-point std::to_chars
implementation on targets whose float/double are not IEEE binary32 and
binary64 respectively, until a proper fallback can be added for such
targets.  This fixes a bootstrap failure on non-IEEE-754 FP targets such
as vax-netbsdelf.

The preprocessor conditions that define the new internal libstdc++
macros for detecting the binary32 and binary64 formats were copied from
the test gcc/testsuite/gcc.dg/float-exact-1.c.

Tested on x86_64-pc-linux-gnu, with and without -m32, and also when
artifically undefining the below new macros.  Does this look OK for
trunk?

libstdc++-v3/ChangeLog:

	* include/bits/c++config (_GLIBCXX_FLOAT_IS_IEEE_BINARY_32):
	Define this macro.
	(_GLIBCXX_DOUBLE_IS_IEEE_BINARY_64): Likewise.
	* include/std/charconv (to_chars): Use the macros to
	conditionally hide the overloads for floating-point types.
	* src/c++17/floating_to_chars.cc: Use the macros to
	conditionally disable this file.
	(floating_type_traits<float>): Remove redundant static assert.
	(floating_type_traits<double>): Likewise.
	* testsuite/20_util/to_chars/double.cc: Use the macros to
	conditionally disable this test.
	* testsuite/20_util/to_chars/float.cc: Likewise.
	* testsuite/20_util/to_chars/long_double.cc: Likewise.  Adjust
	dg-do directives so that we don't execute this test on targets
	with a large long double type and without int128.
---
 libstdc++-v3/include/bits/c++config             | 14 ++++++++++++++
 libstdc++-v3/include/std/charconv               |  2 ++
 libstdc++-v3/src/c++17/floating_to_chars.cc     |  9 +++++----
 .../testsuite/20_util/to_chars/double.cc        |  2 ++
 .../testsuite/20_util/to_chars/float.cc         |  2 ++
 .../testsuite/20_util/to_chars/long_double.cc   | 17 +++++++++++++----
 6 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
index 8cce88aa87b..f54074a2c04 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -688,6 +688,20 @@ namespace std
 # endif
 #endif
 
+// Define if float has the IEEE binary32 format.
+#if __FLT_MANT_DIG__ == 24 \
+  && __FLT_MIN_EXP__ == -125 \
+  && __FLT_MAX_EXP == 128
+# define _GLIBCXX_FLOAT_IS_IEEE_BINARY32 1
+#endif
+
+// Define if double has the IEEE binary64 format.
+#if __DBL_MANT_DIG__ == 53 \
+  && __DBL_MIN_EXP__ == -1021 \
+  && __DBL_MAX_EXP__ == 1024
+# define _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 1
+#endif
+
 #ifdef __has_builtin
 # ifdef __is_identifier
 // Intel and older Clang require !__is_identifier for some built-ins:
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index b57b0a16db2..1f005be47b1 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -704,6 +704,7 @@ namespace __detail
 
   // Floating-point std::to_chars
 
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
   // Overloads for float.
   to_chars_result to_chars(char* __first, char* __last, float __value) noexcept;
   to_chars_result to_chars(char* __first, char* __last, float __value,
@@ -725,6 +726,7 @@ namespace __detail
 			   chars_format __fmt) noexcept;
   to_chars_result to_chars(char* __first, char* __last, long double __value,
 			   chars_format __fmt, int __precision) noexcept;
+#endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc
index b7c31c746cc..6d94d46cc0a 100644
--- a/libstdc++-v3/src/c++17/floating_to_chars.cc
+++ b/libstdc++-v3/src/c++17/floating_to_chars.cc
@@ -22,6 +22,10 @@
 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 // <http://www.gnu.org/licenses/>.
 
+// This implementation crucially assumes float/double have the
+// IEEE binary32/binary64 formats.
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
+
 // Activate __glibcxx_assert within this file to shake out any bugs.
 #define _GLIBCXX_ASSERTIONS 1
 
@@ -109,8 +113,6 @@ namespace
   template<>
     struct floating_type_traits<float>
     {
-      // We (and Ryu) assume float has the IEEE binary32 format.
-      static_assert(__FLT_MANT_DIG__ == 24);
       static constexpr int mantissa_bits = 23;
       static constexpr int exponent_bits = 8;
       static constexpr bool has_implicit_leading_bit = true;
@@ -124,8 +126,6 @@ namespace
   template<>
     struct floating_type_traits<double>
     {
-      // We (and Ryu) assume double has the IEEE binary64 format.
-      static_assert(__DBL_MANT_DIG__ == 53);
       static constexpr int mantissa_bits = 52;
       static constexpr int exponent_bits = 11;
       static constexpr bool has_implicit_leading_bit = true;
@@ -1565,3 +1565,4 @@ _ZSt8to_charsPcS_eSt12chars_formati(char* first, char* last, double value,
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
+#endif // _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/double.cc b/libstdc++-v3/testsuite/20_util/to_chars/double.cc
index 9d1f37d3026..a52d7e7cd61 100644
--- a/libstdc++-v3/testsuite/20_util/to_chars/double.cc
+++ b/libstdc++-v3/testsuite/20_util/to_chars/double.cc
@@ -56952,6 +56952,7 @@ inline constexpr double_to_chars_testcase double_hex_precision_to_chars_test_cas
 void
 test01()
 {
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
   auto handle_testcases = [] (const auto& testcases) {
     for (const auto [value,fmt,precision,correct] : testcases)
       {
@@ -56991,6 +56992,7 @@ test01()
   handle_testcases(double_general_precision_to_chars_test_cases);
 
   handle_testcases(double_hex_precision_to_chars_test_cases);
+#endif
 }
 
 int
diff --git a/libstdc++-v3/testsuite/20_util/to_chars/float.cc b/libstdc++-v3/testsuite/20_util/to_chars/float.cc
index b8901063ea0..25fa4defaa9 100644
--- a/libstdc++-v3/testsuite/20_util/to_chars/float.cc
+++ b/libstdc++-v3/testsuite/20_util/to_chars/float.cc
@@ -4105,6 +4105,7 @@ inline constexpr float_to_chars_testcase float_hex_precision_to_chars_test_cases
 void
 test01()
 {
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
   auto handle_testcases = [] (const auto& testcases) {
     for (const auto [value,fmt,precision,correct] : testcases)
       {
@@ -4133,6 +4134,7 @@ test01()
   handle_testcases(float_fixed_precision_to_chars_test_cases);
   handle_testcases(float_general_precision_to_chars_test_cases);
   handle_testcases(float_hex_precision_to_chars_test_cases);
+#endif
 }
 
 int
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 12ac8ae7822..e34c6c052cf 100644
--- a/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
+++ b/libstdc++-v3/testsuite/20_util/to_chars/long_double.cc
@@ -15,10 +15,15 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// <charconv> is supported in C++14 as a GNU extension, but this test uses C++17
-// hexadecimal floating-point literals.
-// { dg-do run { target c++17 } }
-// { dg-xfail-run-if "Ryu needs __int128" { large_long_double && { ! int128 } } }
+// Although <charconv> is supported in C++14 as a GNU extension, this
+// testcase requires C++17 due to its use hexadecimal floating-point literals.
+// { dg-require-effective-target c++17 }
+
+// On targets with a large long double type and without int128, we forward
+// the long double to_chars overloads to the double to_chars overloads,
+// which leads to a loss in precision in the output relative to printf.
+// { dg-do run { target { { ! large_long_double } || int128 } } }
+// { dg-do compile { target { large_long_double && { ! int128 } } } }
 
 #include <charconv>
 
@@ -38,6 +43,7 @@ using namespace std;
 void
 test01()
 {
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
   const long double hex_testcases[]
     = { nextdownl(numeric_limits<long double>::max()),
 	nextupl(numeric_limits<long double>::min()),
@@ -124,12 +130,14 @@ test01()
 	    VERIFY( !strcmp(to_chars_buffer, printf_buffer+strlen("0x")) );
 	  }
       }
+#endif
 }
 
 // Test the rest of the formatting modes, which go through printf.
 void
 test02()
 {
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
   const long double growth_factor = 1.442695040888963407359924681001892137L;
   for (chars_format fmt : {chars_format::fixed, chars_format::scientific,
 			   chars_format::general})
@@ -189,6 +197,7 @@ test02()
 	      VERIFY( strcmp(to_chars_buffer, nearby_buffer) != 0 );
 	    }
 	}
+#endif
 }
 
 int
-- 
2.30.0.rc0



More information about the Gcc-patches mailing list