libstdc++-v3: Some std::*float*_t charconv and i/ostream overloads

Message ID Y0/0mF4j3680bCG8@tucnak
State Unresolved
Headers
Series libstdc++-v3: Some std::*float*_t charconv and i/ostream overloads |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Oct. 19, 2022, 12:59 p.m. UTC
  Hi!

The following patch adds the easy part of <charconv>, <istream> and
<ostream> changes for extended floats.
In particular, for the first one only overloads where the _Float* has
the same format as float/double/long double and for the latter two
everything but the _GLIBCXX_HAVE_FLOAT128_MATH case.
For charconv, I'm not really familiar with it, I'm pretty sure
we need new libstdc++.so.6 side implementation of from_chars for
{,b}float16_t and for to_chars not really sure but for unspecified precision
if it should emit minimum characters that to_chars then can unambiguously
parse, I think it is less than in the float case.  For float128_t
{to,from}_chars I think we even have it on the library side already, just
ifdefed for powerpc64le only.
For i/o stream operator<</>>, not sure what is better, if not providing
anything at all, or doing what we in the end do if user doesn't override
the virtual functions, or use {to,from}_chars under the hood, something
else?
Besides this, the patch adds some further missed
// { dg-options "-std=gnu++2b" }
spots, I've also noticed I got the formatting wrong in some testcases
by not using spaces around VERIFY conditions and elsewhere by having
space before ( for calls.
The testsuite coverage is limited, I've added test for from_chars because
it was easy to port, but not really sure what to do about to_chars, it has
for float/double huge testcases which would be excessive to repeat.
And for i/ostream not really sure what exactly is worth testing.

Tested on x86_64-linux with --target_board=unix/-std=gnu++23, ok for trunk?

2022-10-19  Jakub Jelinek  <jakub@redhat.com>

	* include/std/charconv (from_chars, to_chars): Add _Float{32,64,128}
	overloads for cases where those types match {float,double,long double}.
	* include/std/istream (basic_istream::operator>>): Add
	_Float{16,32,64,128} and __gnu_cxx::__bfloat16_t overloads.
	* include/std/ostream (basic_ostream::operator<<): Add
	_Float{16,32,64,128} and __gnu_cxx::__bfloat16_t overloads.
	* testsuite/20_util/from_chars/8.cc: New test.
	* testsuite/26_numerics/headers/cmath/nextafter_c++23.cc (test):
	Formatting fixes.
	* testsuite/26_numerics/headers/cmath/functions_std_c++23.cc: Add
	dg-options "-std=gnu++2b".
	(test_functions, main): Formatting fixes.
	* testsuite/26_numerics/headers/cmath/c99_classification_macros_c++23.cc:
	Add dg-options "-std=gnu++2b".


	Jakub
  

Comments

Jonathan Wakely Nov. 1, 2022, 12:18 p.m. UTC | #1
On Wed, 19 Oct 2022 at 13:59, Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> The following patch adds the easy part of <charconv>, <istream> and
> <ostream> changes for extended floats.
> In particular, for the first one only overloads where the _Float* has
> the same format as float/double/long double and for the latter two
> everything but the _GLIBCXX_HAVE_FLOAT128_MATH case.
> For charconv, I'm not really familiar with it, I'm pretty sure
> we need new libstdc++.so.6 side implementation of from_chars for
> {,b}float16_t and for to_chars not really sure but for unspecified precision
> if it should emit minimum characters that to_chars then can unambiguously
> parse, I think it is less than in the float case.  For float128_t
> {to,from}_chars I think we even have it on the library side already, just
> ifdefed for powerpc64le only.
> For i/o stream operator<</>>, not sure what is better, if not providing
> anything at all, or doing what we in the end do if user doesn't override
> the virtual functions, or use {to,from}_chars under the hood, something
> else?
> Besides this, the patch adds some further missed
> // { dg-options "-std=gnu++2b" }
> spots, I've also noticed I got the formatting wrong in some testcases
> by not using spaces around VERIFY conditions and elsewhere by having
> space before ( for calls.
> The testsuite coverage is limited, I've added test for from_chars because
> it was easy to port, but not really sure what to do about to_chars, it has
> for float/double huge testcases which would be excessive to repeat.
> And for i/ostream not really sure what exactly is worth testing.
>
> Tested on x86_64-linux with --target_board=unix/-std=gnu++23, ok for trunk?
>

OK, thanks!
  

Patch

--- libstdc++-v3/include/std/charconv.jj	2022-10-17 12:29:33.805012519 +0200
+++ libstdc++-v3/include/std/charconv	2022-10-19 11:54:03.049992722 +0200
@@ -675,6 +675,45 @@  namespace __detail
   from_chars_result
   from_chars(const char* __first, const char* __last, long double& __value,
 	     chars_format __fmt = chars_format::general) noexcept;
+
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  inline from_chars_result
+  from_chars(const char* __first, const char* __last, _Float32& __value,
+	     chars_format __fmt = chars_format::general) noexcept
+  {
+    float __val;
+    from_chars_result __res = from_chars(__first, __last, __val, __fmt);
+    if (__res.ec == errc{})
+      __value = __val;
+    return __res;
+  }
+#endif
+
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  inline from_chars_result
+  from_chars(const char* __first, const char* __last, _Float64& __value,
+	     chars_format __fmt = chars_format::general) noexcept
+  {
+    double __val;
+    from_chars_result __res = from_chars(__first, __last, __val, __fmt);
+    if (__res.ec == errc{})
+      __value = __val;
+    return __res;
+  }
+#endif
+
+#if defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+  inline from_chars_result
+  from_chars(const char* __first, const char* __last, _Float128& __value,
+	     chars_format __fmt = chars_format::general) noexcept
+  {
+    long double __val;
+    from_chars_result __res = from_chars(__first, __last, __val, __fmt);
+    if (__res.ec == errc{})
+      __value = __val;
+    return __res;
+  }
+#endif
 #endif
 
 #if defined __cpp_lib_to_chars
@@ -701,6 +740,53 @@  namespace __detail
 			   chars_format __fmt) noexcept;
   to_chars_result to_chars(char* __first, char* __last, long double __value,
 			   chars_format __fmt, int __precision) noexcept;
+
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float32 __value) noexcept
+  { return to_chars(__first, __last, float(__value)); }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float32 __value,
+	   chars_format __fmt) noexcept
+  { return to_chars(__first, __last, float(__value), __fmt); }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float32 __value,
+	   chars_format __fmt, int __precision) noexcept
+  { return to_chars(__first, __last, float(__value), __fmt, __precision); }
+#endif
+
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float64 __value) noexcept
+  { return to_chars(__first, __last, double(__value)); }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float64 __value,
+	   chars_format __fmt) noexcept
+  { return to_chars(__first, __last, double(__value), __fmt); }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float64 __value,
+	   chars_format __fmt, int __precision) noexcept
+  { return to_chars(__first, __last, double(__value), __fmt, __precision); }
+#endif
+
+#if defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float128 __value) noexcept
+  { return to_chars(__first, __last, static_cast<long double>(__value)); }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float128 __value,
+	   chars_format __fmt) noexcept
+  {
+    return to_chars(__first, __last, static_cast<long double>(__value), __fmt);
+  }
+  inline to_chars_result
+  to_chars(char* __first, char* __last, _Float128 __value,
+	   chars_format __fmt, int __precision) noexcept
+  {
+    return to_chars(__first, __last, static_cast<long double>(__value), __fmt,
+		    __precision);
+  }
+#endif
 #endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
--- libstdc++-v3/include/std/istream.jj	2022-10-03 18:00:58.977655275 +0200
+++ libstdc++-v3/include/std/istream	2022-10-19 13:16:33.633828491 +0200
@@ -225,6 +225,94 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _M_extract(__f); }
       ///@}
 
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+      __attribute__((__always_inline__))
+      __istream_type&
+      operator>>(_Float16& __f)
+      {
+	float __flt;
+	__istream_type& __ret = _M_extract(__flt);
+	ios_base::iostate __err = ios_base::goodbit;
+	if (__flt < -__FLT16_MAX__)
+	  {
+	    __f = -__FLT16_MAX__;
+	    __err = ios_base::failbit;
+	  }
+	else if (__flt > __FLT16_MAX__)
+	  {
+	    __f = __FLT16_MAX__;
+	    __err = ios_base::failbit;
+	  }
+	else
+	  __f = static_cast<_Float16>(__flt);
+	if (__err)
+	  this->setstate(__err);
+	return __ret;
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+      __attribute__((__always_inline__))
+      __istream_type&
+      operator>>(_Float32& __f)
+      {
+	float __flt;
+	__istream_type& __ret = _M_extract(__flt);
+	__f = static_cast<_Float32> (__flt);
+	return __ret;
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+      __attribute__((__always_inline__))
+      __istream_type&
+      operator>>(_Float64& __f)
+      {
+	double __dbl;
+	__istream_type& __ret = _M_extract(__dbl);
+	__f = static_cast<_Float64> (__dbl);
+	return __ret;
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+      __attribute__((__always_inline__))
+      __istream_type&
+      operator>>(_Float128& __f)
+      {
+	long double __ldbl;
+	__istream_type& __ret = _M_extract(__ldbl);
+	__f = static_cast<_Float128> (__ldbl);
+	return __ret;
+      }
+#endif
+
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+      __attribute__((__always_inline__))
+      __istream_type&
+      operator>>(__gnu_cxx::__bfloat16_t & __f)
+      {
+	float __flt;
+	__istream_type& __ret = _M_extract(__flt);
+	ios_base::iostate __err = ios_base::goodbit;
+	if (__flt < -__BFLT16_MAX__)
+	  {
+	    __f = -__BFLT16_MAX__;
+	    __err = ios_base::failbit;
+	  }
+	else if (__flt > __BFLT16_MAX__)
+	  {
+	    __f = __BFLT16_MAX__;
+	    __err = ios_base::failbit;
+	  }
+	else
+	  __f = static_cast<__gnu_cxx::__bfloat16_t>(__flt);
+	if (__err)
+	  this->setstate(__err);
+	return __ret;
+      }
+#endif
+
       /**
        *  @brief  Basic arithmetic extractors
        *  @param  __p A variable of pointer type.
--- libstdc++-v3/include/std/ostream.jj	2022-10-03 18:00:58.996655019 +0200
+++ libstdc++-v3/include/std/ostream	2022-10-19 12:50:49.861798581 +0200
@@ -235,6 +235,51 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return _M_insert(__f); }
       ///@}
 
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+      __attribute__((__always_inline__))
+      __ostream_type&
+      operator<<(_Float16 __f)
+      {
+	return _M_insert(static_cast<double>(__f));
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+      __attribute__((__always_inline__))
+      __ostream_type&
+      operator<<(_Float32 __f)
+      {
+	return _M_insert(static_cast<double>(__f));
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+      __attribute__((__always_inline__))
+      __ostream_type&
+      operator<<(_Float64 __f)
+      {
+	return _M_insert(static_cast<double>(__f));
+      }
+#endif
+
+#if defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+      __attribute__((__always_inline__))
+      __ostream_type&
+      operator<<(_Float128 __f)
+      {
+	return _M_insert(static_cast<long double>(__f));
+      }
+#endif
+
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+      __attribute__((__always_inline__))
+      __ostream_type&
+      operator<<(__gnu_cxx::__bfloat16_t __f)
+      {
+	return _M_insert(static_cast<double>(__f));
+      }
+#endif
+
       /**
        *  @brief  Pointer arithmetic inserters
        *  @param  __p A variable of pointer type.
--- libstdc++-v3/testsuite/20_util/from_chars/8.cc.jj	2022-10-19 12:02:43.164952247 +0200
+++ libstdc++-v3/testsuite/20_util/from_chars/8.cc	2022-10-19 12:20:55.466149324 +0200
@@ -0,0 +1,367 @@ 
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2b" }
+// { dg-do run { target c++23 } }
+// { dg-add-options ieee }
+
+#include <charconv>
+#include <string>
+#include <limits>
+#include <stdfloat>
+#include <cmath>
+#include <cstdlib>
+#include <testsuite_hooks.h>
+
+// Test std::from_chars floating-point conversions.
+
+#if __cpp_lib_to_chars >= 201611L
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+void
+test01()
+{
+  std::string s;
+  std::float64_t f64;
+  std::from_chars_result res;
+
+  for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific,
+		    std::chars_format::general, std::chars_format::hex })
+  {
+    s = "Info";
+    res = std::from_chars(s.data(), s.data() + s.length(), f64, fmt);
+    VERIFY( std::isinf(f64) );
+    VERIFY( res.ptr == s.data() + 3 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "-INFIN";
+    res = std::from_chars(s.data(), s.data() + s.length(), f64, fmt);
+    VERIFY( std::isinf(f64) );
+    VERIFY( f64 < 0 );
+    VERIFY( res.ptr == s.data() + 4 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "InFiNiTy aNd BeYoNd";
+    res = std::from_chars(s.data(), s.data() + s.length(), f64, fmt);
+    VERIFY( std::isinf(f64) );
+    VERIFY( res.ptr == s.data() + 8 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "nAn";
+    res = std::from_chars(s.data(), s.data() + s.length(), f64, fmt);
+    VERIFY( std::isnan(f64) );
+    VERIFY( res.ptr == s.data() + 3 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "-NAN()";
+    res = std::from_chars(s.data(), s.data() + s.length(), f64, fmt);
+    VERIFY( std::isnan(f64) );
+    VERIFY( res.ptr == s.data() + s.length() );
+    VERIFY( res.ec == std::errc{} );
+  }
+}
+
+void
+test02()
+{
+  std::string s;
+  std::float64_t f64 = 1.0f64;
+  std::from_chars_result res;
+
+  s = "0x123";
+  res = std::from_chars(s.data(), s.data() + s.length(), f64);
+  VERIFY( f64 == 0.0f64 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::fixed);
+  VERIFY( f64 == 0.0f64 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::scientific);
+  VERIFY( f64 == 1.0f64 );
+  VERIFY( res.ptr == s.data() );
+  VERIFY( res.ec == std::errc::invalid_argument );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::general);
+  VERIFY( f64 == 0.0f64 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::hex);
+  VERIFY( f64 == 0.0f64 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+}
+
+void
+test03()
+{
+  std::string s;
+  std::float64_t f64 = 1.0f64;
+  std::from_chars_result res;
+
+  s = "0.5e+2azzz";
+  res = std::from_chars(s.data(), s.data() + s.length(), f64);
+  VERIFY( f64 == 0.5e+2f64 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::fixed);
+  VERIFY( f64 == 0.5f64 );
+  VERIFY( res.ptr == s.data() + 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::scientific);
+  VERIFY( f64 == 0.5e+2f64 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0f64;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::general);
+  VERIFY( f64 == 0.5e+2f64 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  f64 = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::hex);
+  VERIFY( f64 == 0x0.5Ep0f64 );
+  VERIFY( res.ptr == s.data() + 4 );
+  VERIFY( res.ec == std::errc{} );
+
+  s = "1.Ap-2zzz";
+  res = std::from_chars(s.data(), s.data() + s.length(), f64,
+			std::chars_format::hex);
+  VERIFY( f64 == 0.40625f64 );
+  VERIFY( res.ptr == s.data() + s.length() - 3 );
+  VERIFY( res.ec == std::errc{} );
+}
+
+void
+test04()
+{
+  // Huge input strings
+  std::string s(1000, '0');
+  std::float64_t f64 = 1.0f64;
+  std::from_chars_result res;
+  res = std::from_chars(s.data(), s.data() + s.length(), f64);
+  VERIFY( res.ptr == s.data() + s.length() );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( f64 == 0.0f64 );
+
+  s += ".5";
+  res = std::from_chars(s.data(), s.data() + s.length(), f64);
+  VERIFY( res.ptr == s.data() + s.length() );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( f64 == 0.5f64 );
+
+  s += "e2";
+  auto len = s.length();
+  s += std::string(1000, 'a');
+  res = std::from_chars(s.data(), s.data() + s.length(), f64);
+  VERIFY( res.ptr == s.data() + len );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( f64 == 50.f64 );
+}
+#endif
+
+using std::to_string;
+
+#ifdef __GLIBCXX_TYPE_INT_N_0
+std::string
+to_string(unsigned __GLIBCXX_TYPE_INT_N_0 val)
+{
+  using Limits = std::numeric_limits<unsigned __GLIBCXX_TYPE_INT_N_0>;
+  std::string s(Limits::digits10+2, '0');
+  for (auto iter = s.end(); val != 0; val /= 10)
+    *--iter = '0' + (val % 10);
+  return s;
+}
+#endif
+
+template<typename FloatT>
+void
+test_small_num()
+{
+  std::from_chars_result res;
+  FloatT flt;
+
+  // Small integer values that are exactly representable
+
+  for (int i = 0; i < 100; ++i)
+  {
+    std::string s = to_string(i);
+    int len = s.length();
+    s += "123";
+    const char* s1 = s.c_str();
+    const char* s1_end = s1 + len;
+
+    for (auto fmt : { std::chars_format::fixed,
+		      std::chars_format::general,
+		      std::chars_format::hex })
+    {
+      if (fmt == std::chars_format::hex && i > 9)
+	continue;
+
+      res = std::from_chars(s1, s1_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s1_end );
+      VERIFY( flt == i );
+    }
+
+    if (i > 9)
+      continue;
+
+    // Test single-digit integers with small exponents.
+
+    const char s2[] = { '.', *s1, 'e', '0', '0', '0', '1' };
+    const char* s2_end = s2 + sizeof(s2);
+
+    const char s3[] = { *s1, '0', 'e', '-', '0', '0', '1' };
+    const char* s3_end = s3 + sizeof(s3);
+
+    for (auto fmt : { std::chars_format::scientific,
+		      std::chars_format::general })
+    {
+      res = std::from_chars(s2, s2_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s2_end );
+      VERIFY( flt == i );
+
+      res = std::from_chars(s3, s3_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s3_end );
+      VERIFY( flt == i );
+    }
+  }
+}
+
+void
+test05()
+{
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  test_small_num<std::float32_t>();
+#endif
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  test_small_num<std::float64_t>();
+#endif
+#if defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+  test_small_num<std::float128_t>();
+#endif
+}
+
+template<typename FloatT, typename UIntT>
+void
+test_max_mantissa()
+{
+  using Float_limits = std::numeric_limits<FloatT>;
+  using UInt_limits = std::numeric_limits<UIntT>;
+
+  if (Float_limits::is_iec559 && Float_limits::digits < UInt_limits::digits)
+  {
+#ifdef _GLIBCXX_USE_C99_MATH_TR1
+    std::printf("Testing %d-bit float, using %zu-bit integer\n",
+	Float_limits::digits + (int)std::log2(Float_limits::max_exponent) + 1,
+	sizeof(UIntT) * __CHAR_BIT__);
+#endif
+
+    std::from_chars_result res;
+    FloatT flt;
+
+    for (int i = 0; i < 10; ++i)
+    {
+      // (1 << digits) - 1 is the maximum value of the mantissa
+      const auto val = ((UIntT)1 << Float_limits::digits) - 1 - i;
+      std::string s = to_string(val);
+      auto len = s.length();
+      s += "000"; // these should be ignored
+      for (auto fmt : { std::chars_format::fixed,
+			std::chars_format::general })
+      {
+	res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+	VERIFY( res.ec == std::errc{} );
+	VERIFY( res.ptr == s.data() + len );
+	VERIFY( flt == val );
+      }
+      s.resize(len);
+      const auto orig_len = len;
+      s += "e+000";
+      len = s.length();
+      s += "111";
+      for (auto fmt : { std::chars_format::scientific,
+			std::chars_format::general })
+      {
+	res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+	VERIFY( res.ec == std::errc{} );
+	VERIFY( res.ptr == s.data() + len );
+	VERIFY( flt == val );
+
+	std::string s2 = s.substr(0, len - 5);
+	s2.insert(s2.begin() + orig_len - 1, '.');
+	s2 += "e000000000001";
+	res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+	VERIFY( res.ec == std::errc{} );
+	VERIFY( res.ptr == s.data() + len );
+	VERIFY( flt == val );
+      }
+    }
+  }
+}
+
+void
+test06()
+{
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  test_max_mantissa<std::float32_t, unsigned long>();
+#endif
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  test_max_mantissa<std::float64_t, unsigned long long>();
+#endif
+#if defined(__GLIBCXX_TYPE_INT_N_0) \
+    && defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128)
+  test_max_mantissa<std::float128_t, unsigned __GLIBCXX_TYPE_INT_N_0>();
+#endif
+}
+#endif
+
+int
+main()
+{
+#if __cpp_lib_to_chars >= 201611L
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  test01();
+  test02();
+  test03();
+  test04();
+#endif
+  test05();
+  test06();
+#endif
+}
--- libstdc++-v3/testsuite/26_numerics/headers/cmath/nextafter_c++23.cc.jj	2022-10-19 11:24:54.748632989 +0200
+++ libstdc++-v3/testsuite/26_numerics/headers/cmath/nextafter_c++23.cc	2022-10-19 12:18:01.973503400 +0200
@@ -29,77 +29,77 @@  test ()
 {
   using lim = std::numeric_limits<T>;
   T t0 = std::nextafter(T(-0.0), T(2.0));
-  VERIFY(t0 == lim::denorm_min());
+  VERIFY( t0 == lim::denorm_min() );
   T t1 = std::nextafter(T(), T(1.0));
-  VERIFY(t1 == lim::denorm_min());
+  VERIFY( t1 == lim::denorm_min() );
   T t2 = std::nextafter(T(), T());
-  VERIFY(t2 == T() && !std::signbit(t2));
+  VERIFY( t2 == T() && !std::signbit(t2) );
   T t3 = std::nextafter(lim::denorm_min(), T(-2.0));
-  VERIFY(t3 == T() && !std::signbit(t3));
+  VERIFY( t3 == T() && !std::signbit(t3) );
   T t4 = std::nextafter(lim::min(), T(-0.0));
-  VERIFY(std::fpclassify(t4) == FP_SUBNORMAL && t4 > T());
+  VERIFY( std::fpclassify(t4) == FP_SUBNORMAL && t4 > T() );
   T t5 = std::nextafter(t4, T(1.0));
-  VERIFY(t5 == lim::min());
+  VERIFY( t5 == lim::min() );
   T t6 = std::nextafter(lim::min(), lim::infinity());
-  VERIFY(std::fpclassify(t6) == FP_NORMAL && t6 > lim::min());
+  VERIFY( std::fpclassify(t6) == FP_NORMAL && t6 > lim::min() );
   T t7 = std::nextafter(t6, -lim::infinity());
-  VERIFY(t7 == lim::min());
+  VERIFY( t7 == lim::min() );
   T t8 = std::nextafter(T(16.0), T(16.5));
-  VERIFY(t8 > t7);
+  VERIFY( t8 > t7 );
   T t9 = std::nextafter(t8, T(15.5));
-  VERIFY(t9 == T(16.0));
+  VERIFY( t9 == T(16.0) );
   T t10 = std::nextafter(lim::max(), T(-0.5));
-  VERIFY(std::fpclassify(t10) == FP_NORMAL && t10 < lim::max());
+  VERIFY( std::fpclassify(t10) == FP_NORMAL && t10 < lim::max() );
   T t11 = std::nextafter(t10, lim::infinity());
-  VERIFY(t11 == lim::max());
+  VERIFY( t11 == lim::max() );
   T t12 = std::nextafter(t11, lim::infinity());
-  VERIFY(std::fpclassify(t12) == FP_INFINITE && !std::signbit(t12));
+  VERIFY( std::fpclassify(t12) == FP_INFINITE && !std::signbit(t12) );
   T t13 = std::nextafter(lim::infinity(), t12);
-  VERIFY(t13 == t12);
+  VERIFY( t13 == t12 );
   T t14 = std::nextafter(t13, T(1.0));
-  VERIFY(t14 == lim::max());
+  VERIFY( t14 == lim::max() );
   T t15 = std::nextafter(lim::quiet_NaN(), T());
-  VERIFY(std::fpclassify(t15) == FP_NAN);
+  VERIFY( std::fpclassify(t15) == FP_NAN );
   T t16 = std::nextafter(T(17.0), lim::quiet_NaN());
-  VERIFY(std::fpclassify(t16) == FP_NAN);
+  VERIFY( std::fpclassify(t16) == FP_NAN );
   T t17 = std::nextafter(T(), T(-0.0));
-  VERIFY(t17 == T() && std::signbit(t17));
+  VERIFY( t17 == T() && std::signbit(t17) );
   T t20 = std::nextafter(T(-0.0), T(-2.0));
-  VERIFY(t20 == -lim::denorm_min());
+  VERIFY( t20 == -lim::denorm_min() );
   T t21 = std::nextafter(T(), T(-1.0));
-  VERIFY(t21 == -lim::denorm_min());
+  VERIFY( t21 == -lim::denorm_min() );
   T t22 = std::nextafter(T(-0.0), T(-0.0));
-  VERIFY(t22 == T() && std::signbit(t22));
+  VERIFY( t22 == T() && std::signbit(t22) );
   T t23 = std::nextafter(-lim::denorm_min(), T(2.0));
-  VERIFY(t23 == T() && std::signbit(t23));
+  VERIFY( t23 == T() && std::signbit(t23) );
   T t24 = std::nextafter(-lim::min(), T());
-  VERIFY(std::fpclassify(t24) == FP_SUBNORMAL && t24 < T());
+  VERIFY( std::fpclassify(t24) == FP_SUBNORMAL && t24 < T() );
   T t25 = std::nextafter(t24, T(-1.0));
-  VERIFY(t25 == -lim::min());
+  VERIFY( t25 == -lim::min() );
   T t26 = std::nextafter(-lim::min(), -lim::infinity());
-  VERIFY(std::fpclassify(t26) == FP_NORMAL && t26 < -lim::min());
+  VERIFY( std::fpclassify(t26) == FP_NORMAL && t26 < -lim::min() );
   T t27 = std::nextafter(t26, lim::infinity());
-  VERIFY(t27 == -lim::min());
+  VERIFY( t27 == -lim::min() );
   T t28 = std::nextafter(T(-16.0), T(-16.5));
-  VERIFY(t28 < t27);
+  VERIFY( t28 < t27 );
   T t29 = std::nextafter(t28, T(-15.5));
-  VERIFY(t29 == T(-16.0));
+  VERIFY( t29 == T(-16.0) );
   T t30 = std::nextafter(-lim::max(), T(0.5));
-  VERIFY(std::fpclassify(t30) == FP_NORMAL && t30 > -lim::max());
+  VERIFY( std::fpclassify(t30) == FP_NORMAL && t30 > -lim::max() );
   T t31 = std::nextafter(t30, -lim::infinity());
-  VERIFY(t31 == -lim::max());
+  VERIFY( t31 == -lim::max() );
   T t32 = std::nextafter(t31, -lim::infinity());
-  VERIFY(std::fpclassify(t32) == FP_INFINITE && std::signbit(t32));
+  VERIFY( std::fpclassify(t32) == FP_INFINITE && std::signbit(t32) );
   T t33 = std::nextafter(-lim::infinity(), t32);
-  VERIFY(t33 == t32);
+  VERIFY( t33 == t32 );
   T t34 = std::nextafter(t33, T(-1.0));
-  VERIFY(t34 == -lim::max());
+  VERIFY( t34 == -lim::max() );
   T t35 = std::nextafter(-lim::quiet_NaN(), T());
-  VERIFY(std::fpclassify(t35) == FP_NAN);
+  VERIFY( std::fpclassify(t35) == FP_NAN );
   T t36 = std::nextafter(T(-17.0), lim::quiet_NaN());
-  VERIFY(std::fpclassify(t36) == FP_NAN);
+  VERIFY( std::fpclassify(t36) == FP_NAN );
   T t37 = std::nextafter(T(-0.0), T());
-  VERIFY(t37 == T() && !std::signbit(t37));
+  VERIFY( t37 == T() && !std::signbit(t37) );
 }
 
 int
--- libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc.jj	2022-10-19 11:23:51.000000000 +0200
+++ libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc	2022-10-19 11:36:26.760280020 +0200
@@ -15,6 +15,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+// { dg-options "-std=gnu++2b" }
 // { dg-do link { target c++23 } }
 
 #include <stdfloat>
@@ -22,91 +23,91 @@ 
 
 template <typename T>
 __attribute__((__noipa__)) void
-test_functions (T *p, int *q, long int *r, long long int *s)
+test_functions(T *p, int *q, long int *r, long long int *s)
 {
-  p[0] = std::acos (p[0]);
-  p[1] = std::asin (p[1]);
-  p[2] = std::atan (p[2]);
-  p[3] = std::cos (p[3]);
-  p[4] = std::sin (p[4]);
-  p[5] = std::tan (p[5]);
-  p[6] = std::acosh (p[6]);
-  p[7] = std::asinh (p[7]);
-  p[8] = std::atanh (p[8]);
-  p[9] = std::cosh (p[9]);
-  p[10] = std::sinh (p[10]);
-  p[11] = std::tanh (p[11]);
-  p[12] = std::exp (p[12]);
-  p[13] = std::exp2 (p[13]);
-  p[14] = std::expm1 (p[14]);
-  p[15] = std::log (p[15]);
-  p[16] = std::log10 (p[16]);
-  p[17] = std::log1p (p[17]);
-  p[18] = std::log2 (p[18]);
-  p[19] = std::logb (p[19]);
-  p[20] = std::cbrt (p[20]);
-  p[21] = std::fabs (p[21]);
-  p[22] = std::sqrt (p[22]);
-  p[23] = std::erf (p[23]);
-  p[24] = std::erfc (p[24]);
-  p[25] = std::lgamma (p[25]);
-  p[26] = std::tgamma (p[26]);
-  p[27] = std::ceil (p[27]);
-  p[28] = std::floor (p[28]);
-  p[29] = std::nearbyint (p[29]);
-  p[30] = std::rint (p[30]);
-  p[31] = std::round (p[31]);
-  p[32] = std::trunc (p[32]);
-  p[33] = std::atan2 (p[33], p[100]);
-  p[34] = std::hypot (p[34], p[101]);
-  p[35] = std::pow (p[35], p[102]);
-  p[36] = std::fmod (p[36], p[103]);
-  p[37] = std::remainder (p[37], p[104]);
-  p[38] = std::copysign (p[38], p[105]);
-  p[39] = std::nextafter (p[39], p[106]);
-  p[40] = std::fdim (p[40], p[107]);
-  p[41] = std::fmax (p[41], p[108]);
-  p[42] = std::fmin (p[42], p[109]);
-  p[43] = std::atan2 (p[43], p[110]);
-  p[44] = std::frexp (p[44], q + 0);
-  q[1] = std::ilogb (p[45]);
-  p[46] = std::ldexp (p[46], q[2]);
-  p[47] = std::modf (p[47], p + 111);
-  p[48] = std::scalbn (p[48], q[3]);
-  p[49] = std::scalbln (p[49], r[0]);
-  p[50] = std::hypot (p[50], p[111], p[112]);
-  r[1] = std::lrint (p[51]);
-  s[0] = std::llrint (p[52]);
-  r[2] = std::lround (p[53]);
-  s[1] = std::llround (p[54]);
-  p[55] = std::remquo (p[55], p[113], q + 4);
-  p[56] = std::fma (p[56], p[114], p[115]);
-  p[57] = std::lerp (p[57], p[116], p[117]);
-  p[58] = std::assoc_laguerre (q[5], q[6], p[58]);
-  p[59] = std::assoc_legendre (q[7], q[8], p[59]);
-  p[60] = std::beta (p[60], p[118]);
-  p[61] = std::comp_ellint_1 (p[61]);
-  p[62] = std::comp_ellint_2 (p[62]);
-  p[63] = std::comp_ellint_3 (p[63], p[119]);
-  p[64] = std::cyl_bessel_i (p[64], p[120]);
-  p[65] = std::cyl_bessel_j (p[65], p[121]);
-  p[66] = std::cyl_bessel_k (p[66], p[122]);
-  p[67] = std::cyl_neumann (p[67], p[123]);
-  p[68] = std::ellint_1 (p[68], p[124]);
-  p[69] = std::ellint_2 (p[69], p[125]);
-  p[70] = std::ellint_3 (p[70], p[126], p[127]);
-  p[71] = std::expint (p[71]);
-  p[72] = std::hermite (q[9], p[72]);
-  p[73] = std::laguerre (q[10], p[73]);
-  p[74] = std::legendre (q[11], p[72]);
-  p[75] = std::riemann_zeta (p[75]);
-  p[76] = std::sph_bessel (q[12], p[76]);
-  p[77] = std::sph_legendre (q[13], q[14], p[77]);
-  p[78] = std::sph_neumann (q[15], p[78]);
+  p[0] = std::acos(p[0]);
+  p[1] = std::asin(p[1]);
+  p[2] = std::atan(p[2]);
+  p[3] = std::cos(p[3]);
+  p[4] = std::sin(p[4]);
+  p[5] = std::tan(p[5]);
+  p[6] = std::acosh(p[6]);
+  p[7] = std::asinh(p[7]);
+  p[8] = std::atanh(p[8]);
+  p[9] = std::cosh(p[9]);
+  p[10] = std::sinh(p[10]);
+  p[11] = std::tanh(p[11]);
+  p[12] = std::exp(p[12]);
+  p[13] = std::exp2(p[13]);
+  p[14] = std::expm1(p[14]);
+  p[15] = std::log(p[15]);
+  p[16] = std::log10(p[16]);
+  p[17] = std::log1p(p[17]);
+  p[18] = std::log2(p[18]);
+  p[19] = std::logb(p[19]);
+  p[20] = std::cbrt(p[20]);
+  p[21] = std::fabs(p[21]);
+  p[22] = std::sqrt(p[22]);
+  p[23] = std::erf(p[23]);
+  p[24] = std::erfc(p[24]);
+  p[25] = std::lgamma(p[25]);
+  p[26] = std::tgamma(p[26]);
+  p[27] = std::ceil(p[27]);
+  p[28] = std::floor(p[28]);
+  p[29] = std::nearbyint(p[29]);
+  p[30] = std::rint(p[30]);
+  p[31] = std::round(p[31]);
+  p[32] = std::trunc(p[32]);
+  p[33] = std::atan2(p[33], p[100]);
+  p[34] = std::hypot(p[34], p[101]);
+  p[35] = std::pow(p[35], p[102]);
+  p[36] = std::fmod(p[36], p[103]);
+  p[37] = std::remainder(p[37], p[104]);
+  p[38] = std::copysign(p[38], p[105]);
+  p[39] = std::nextafter(p[39], p[106]);
+  p[40] = std::fdim(p[40], p[107]);
+  p[41] = std::fmax(p[41], p[108]);
+  p[42] = std::fmin(p[42], p[109]);
+  p[43] = std::atan2(p[43], p[110]);
+  p[44] = std::frexp(p[44], q + 0);
+  q[1] = std::ilogb(p[45]);
+  p[46] = std::ldexp(p[46], q[2]);
+  p[47] = std::modf(p[47], p + 111);
+  p[48] = std::scalbn(p[48], q[3]);
+  p[49] = std::scalbln(p[49], r[0]);
+  p[50] = std::hypot(p[50], p[111], p[112]);
+  r[1] = std::lrint(p[51]);
+  s[0] = std::llrint(p[52]);
+  r[2] = std::lround(p[53]);
+  s[1] = std::llround(p[54]);
+  p[55] = std::remquo(p[55], p[113], q + 4);
+  p[56] = std::fma(p[56], p[114], p[115]);
+  p[57] = std::lerp(p[57], p[116], p[117]);
+  p[58] = std::assoc_laguerre(q[5], q[6], p[58]);
+  p[59] = std::assoc_legendre(q[7], q[8], p[59]);
+  p[60] = std::beta(p[60], p[118]);
+  p[61] = std::comp_ellint_1(p[61]);
+  p[62] = std::comp_ellint_2(p[62]);
+  p[63] = std::comp_ellint_3(p[63], p[119]);
+  p[64] = std::cyl_bessel_i(p[64], p[120]);
+  p[65] = std::cyl_bessel_j(p[65], p[121]);
+  p[66] = std::cyl_bessel_k(p[66], p[122]);
+  p[67] = std::cyl_neumann(p[67], p[123]);
+  p[68] = std::ellint_1(p[68], p[124]);
+  p[69] = std::ellint_2(p[69], p[125]);
+  p[70] = std::ellint_3(p[70], p[126], p[127]);
+  p[71] = std::expint(p[71]);
+  p[72] = std::hermite(q[9], p[72]);
+  p[73] = std::laguerre(q[10], p[73]);
+  p[74] = std::legendre(q[11], p[72]);
+  p[75] = std::riemann_zeta(p[75]);
+  p[76] = std::sph_bessel(q[12], p[76]);
+  p[77] = std::sph_legendre(q[13], q[14], p[77]);
+  p[78] = std::sph_neumann(q[15], p[78]);
 }
 
 int
-main ()
+main()
 {
   int q[16] = {};
   long int r[16] = {};
@@ -114,19 +115,19 @@  main ()
 #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
   {
     std::float16_t p[128] = {};
-    test_functions (p, q, r, s);
+    test_functions(p, q, r, s);
   }
 #endif
 #if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
   {
     std::float32_t p[128] = {};
-    test_functions (p, q, r, s);
+    test_functions(p, q, r, s);
   }
 #endif
 #if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
   {
     std::float64_t p[128] = {};
-    test_functions (p, q, r, s);
+    test_functions(p, q, r, s);
   }
 #endif
 #if defined(__STDCPP_FLOAT128_T__) \
@@ -134,13 +135,13 @@  main ()
 	|| defined(_GLIBCXX_HAVE_FLOAT128_MATH))
   {
     std::float128_t p[128] = {};
-    test_functions (p, q, r, s);
+    test_functions(p, q, r, s);
   }
 #endif
 #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
   {
     std::bfloat16_t p[128] = {};
-    test_functions (p, q, r, s);
+    test_functions(p, q, r, s);
   }
 #endif
 }
--- libstdc++-v3/testsuite/26_numerics/headers/cmath/c99_classification_macros_c++23.cc.jj	2022-10-18 11:35:55.000000000 +0200
+++ libstdc++-v3/testsuite/26_numerics/headers/cmath/c99_classification_macros_c++23.cc	2022-10-19 11:34:48.288610909 +0200
@@ -15,6 +15,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+// { dg-options "-std=gnu++2b" }
 // { dg-do link { target c++23 } }
 // { dg-excess-errors "" { target uclibc } }