public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-3372] libstdc++-v3: Implement {, b}float16_t nextafter and some fixes [PR106652]
@ 2022-10-19  9:26 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2022-10-19  9:26 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:ba281da28d34f9a78a07f6ee56ad2c754447966e

commit r13-3372-gba281da28d34f9a78a07f6ee56ad2c754447966e
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Wed Oct 19 11:25:03 2022 +0200

    libstdc++-v3: Implement {,b}float16_t nextafter and some fixes [PR106652]
    
    The following patch implements nextafter for std::{,b}float16_t,
    though right now only without constexpr support, and adds a testcase for it.
    The testcase unfortunately relevealed I've screwed up testing of my last
    patch.  I've tested earlier version of the patch with
    --target_board=unix/-std=c++23
    but didn't test the final version with that RUNTESTFLAGS, so missed
    an invalid call to std::sph_neumann (too many arguments) in the test.
    And, I've made a typo in the guard for numeric_limits (the reason
    for the guard is I wanted to avoid defining a large macro that nothing
    will then use because the std::{,b}float*_t types are C++23 only) and
    so numeric_limits wasn't specialized for the types at all but
    testsuite/18_support/headers/limits/synopsis_cxx23.cc test didn't
    detect that.
    In the nextafter implementation I'm calling __builtin_nextafterf
    to get various required side-effects for nextafter from 0/-0, or from max
    to inf or from min to largest subnormal to avoid needing to set errno
    inline, or use inline asm specific for each processor to force math
    evaluation barriers.  Dunno if
      #ifdef __INT16_TYPE__
        using __float16_int_type = __INT16_TYPE__;
      #else
        using __float16_int_type = short int;
      #endif
    isn't too ugly, perhaps we could just blindly use short int and hope
    or even assert it has the same size as _Float16 or __gnu_cxx::__bfloat16_t?
    Only aarch64, arm, csky, gcn, x86, nvptx and riscv support these types
    and all of them have 16-bit short (I think the only target with some
    other short size is avr with certain command line switches where both
    short and int are 8-bit, but such mode isn't compatible with C and C++
    requirements).
    
    2022-10-19  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/106652
            * include/std/limits: Fix a typo, 202202L -> 202002L.
            (numeric_limits::<_Float16>::radix, numeric_limits::<_Float32>::radix,
            numeric_limits::<_Float64>::radix, numeric_limits::<_Float128>::radix,
            numeric_limits::<__gnu_cxx::__bfloat16_t>::radix): Use __FLT_RADIX__
            macro instead of type specific macros.
            * include/c_global/cmath (nextafter(_Float16, _Float16)): New
            overload.
            (nextafter(__gnu_cxx::__bfloat16_t, __gnu_cxx::__bfloat16_t)):
            Likewise.
            * testsuite/26_numerics/headers/cmath/functions_std_c++23.cc
            (test_functions): Uncomment nextafter test.  Fix up sph_neumann call.
            * testsuite/26_numerics/headers/cmath/nextafter_c++23.cc: New test.

Diff:
---
 libstdc++-v3/include/c_global/cmath                |  94 +++++++++++++++-
 libstdc++-v3/include/std/limits                    |   7 +-
 .../headers/cmath/functions_std_c++23.cc           |   6 +-
 .../26_numerics/headers/cmath/nextafter_c++23.cc   | 125 +++++++++++++++++++++
 4 files changed, 223 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/include/c_global/cmath b/libstdc++-v3/include/c_global/cmath
index 81635a03f7f..555d6440849 100644
--- a/libstdc++-v3/include/c_global/cmath
+++ b/libstdc++-v3/include/c_global/cmath
@@ -2755,7 +2755,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   nearbyint(_Float16 __x)
   { return _Float16(__builtin_nearbyintf(__x)); }
 
-  // nextafter not implemented so far.
+  inline _Float16
+  nextafter(_Float16 __x, _Float16 __y)
+  {
+#ifdef __INT16_TYPE__
+    using __float16_int_type = __INT16_TYPE__;
+#else
+    using __float16_int_type = short int;
+#endif
+    __float16_int_type __hx, __hy, __ix, __iy;
+    __builtin_memcpy(&__hx, &__x, sizeof(__x));
+    __builtin_memcpy(&__hy, &__y, sizeof(__x));
+    __ix = __hx & 0x7fff;	// |x|
+    __iy = __hy & 0x7fff;	// |y|
+    if (__ix > 0x7c00 || __iy > 0x7c00) // x or y is NaN
+      return __x + __y;
+    if (__x == __y)
+      return __y;		// x == y, return y
+    if (__ix == 0)		// x == 0
+      {
+	__hy = (__hy & 0x8000) | 1;	// return +-__FLT16_DENORM_MIN__
+	__builtin_memcpy(&__x, &__hy, sizeof(__x));
+	__builtin_nextafterf(0.0f, 1.0f);	// raise underflow
+	return __x;
+      }
+    if (__hx >= 0)		// x > 0
+      {
+	if (__hx > __hy)	// x > y, x -= ulp
+	  --__hx;
+	else			// x < y, x += ulp
+	  ++__hx;
+      }
+    else			// x < 0
+      {
+	if (__hy >= 0 || __hx > __hy)	// x < y, x -= ulp
+	  --__hx;
+	else			// x > y, x += ulp
+	  ++__hx;
+      }
+    __hy = __hx & 0x7c00;
+    if (__hy >= 0x7c00)
+      __builtin_nextafterf(__FLT_MAX__, __builtin_inff());	// overflow
+    else if (__hy < 0x0400)
+      __builtin_nextafterf(__FLT_MIN__, 0.0f);	// underflow
+    __builtin_memcpy(&__x, &__hx, sizeof(__x));
+    return __x;
+  }
 
   constexpr _Float16
   remainder(_Float16 __x, _Float16 __y)
@@ -3426,7 +3471,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   nearbyint(__gnu_cxx::__bfloat16_t __x)
   { return __gnu_cxx::__bfloat16_t(__builtin_nearbyintf(__x)); }
 
-  // nextafter not implemented so far.
+  inline __gnu_cxx::__bfloat16_t
+  nextafter(__gnu_cxx::__bfloat16_t __x, __gnu_cxx::__bfloat16_t __y)
+  {
+#ifdef __INT16_TYPE__
+    using __bfloat16_int_type = __INT16_TYPE__;
+#else
+    using __bfloat16_int_type = short int;
+#endif
+    __bfloat16_int_type __hx, __hy, __ix, __iy;
+    __builtin_memcpy(&__hx, &__x, sizeof(__x));
+    __builtin_memcpy(&__hy, &__y, sizeof(__x));
+    __ix = __hx & 0x7fff;	// |x|
+    __iy = __hy & 0x7fff;	// |y|
+    if (__ix > 0x7f80 || __iy > 0x7f80) // x or y is NaN
+      return __x + __y;
+    if (__x == __y)
+      return __y;		// x == y, return y
+    if (__ix == 0)		// x == 0
+      {
+	__hy = (__hy & 0x8000) | 1;	// return +-__BFLT16_DENORM_MIN__
+	__builtin_memcpy(&__x, &__hy, sizeof(__x));
+	__builtin_nextafterf(0.0f, 1.0f);	// raise underflow
+	return __x;
+      }
+    if (__hx >= 0)		// x > 0
+      {
+	if (__hx > __hy)	// x > y, x -= ulp
+	  --__hx;
+	else			// x < y, x += ulp
+	  ++__hx;
+      }
+    else			// x < 0
+      {
+	if (__hy >= 0 || __hx > __hy)	// x < y, x -= ulp
+	  --__hx;
+	else			// x > y, x += ulp
+	  ++__hx;
+      }
+    __hy = __hx & 0x7f80;
+    if (__hy >= 0x7f80)
+      __builtin_nextafterf(__FLT_MAX__, __builtin_inff());	// overflow
+    else if (__hy < 0x0080)
+      __builtin_nextafterf(__FLT_MIN__, 0.0f);	// underflow
+    __builtin_memcpy(&__x, &__hx, sizeof(__x));
+    return __x;
+  }
 
   constexpr __gnu_cxx::__bfloat16_t
   remainder(__gnu_cxx::__bfloat16_t __x, __gnu_cxx::__bfloat16_t __y)
diff --git a/libstdc++-v3/include/std/limits b/libstdc++-v3/include/std/limits
index 61b6c3b0f2d..9dde9f39b73 100644
--- a/libstdc++-v3/include/std/limits
+++ b/libstdc++-v3/include/std/limits
@@ -1890,7 +1890,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #undef __glibcxx_long_double_traps
 #undef __glibcxx_long_double_tinyness_before
 
-#if __cplusplus > 202202L
+#if __cplusplus > 202002L
 
 #define __glibcxx_concat3_(P,M,S) P ## M ## S
 #define __glibcxx_concat3(P,M,S) __glibcxx_concat3_ (P,M,S)
@@ -1924,8 +1924,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static constexpr bool is_signed = true;				\
       static constexpr bool is_integer = false;				\
       static constexpr bool is_exact = false;				\
-      static constexpr int radix					\
-	= __glibcxx_concat3 (__FLT, BITSIZE, _RADIX__);			\
+      static constexpr int radix = __FLT_RADIX__;			\
 									\
       static constexpr _Float##BITSIZE					\
       epsilon() noexcept						\
@@ -2023,7 +2022,7 @@ __glibcxx_float_n(128)
       static constexpr bool is_signed = true;
       static constexpr bool is_integer = false;
       static constexpr bool is_exact = false;
-      static constexpr int radix = __BFLT16_RADIX__;
+      static constexpr int radix = __FLT_RADIX__;
 
       static constexpr __gnu_cxx::__bfloat16_t
       epsilon() noexcept
diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc b/libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc
index bc8866227ef..190f8d18aea 100644
--- a/libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc
+++ b/libstdc++-v3/testsuite/26_numerics/headers/cmath/functions_std_c++23.cc
@@ -63,7 +63,7 @@ test_functions (T *p, int *q, long int *r, long long int *s)
   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[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]);
@@ -102,13 +102,13 @@ test_functions (T *p, int *q, long int *r, long long int *s)
   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], q[16], p[78]);
+  p[78] = std::sph_neumann (q[15], p[78]);
 }
 
 int
 main ()
 {
-  int q[17] = {};
+  int q[16] = {};
   long int r[16] = {};
   long long int s[16] = {};
 #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cmath/nextafter_c++23.cc b/libstdc++-v3/testsuite/26_numerics/headers/cmath/nextafter_c++23.cc
new file mode 100644
index 00000000000..45aa3b74b25
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/headers/cmath/nextafter_c++23.cc
@@ -0,0 +1,125 @@
+// Copyright (C) 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 } }
+
+#include <stdfloat>
+#include <cmath>
+#include <limits>
+#include <testsuite_hooks.h>
+
+template <typename T>
+void
+test ()
+{
+  using lim = std::numeric_limits<T>;
+  T t0 = std::nextafter(T(-0.0), T(2.0));
+  VERIFY(t0 == lim::denorm_min());
+  T t1 = std::nextafter(T(), T(1.0));
+  VERIFY(t1 == lim::denorm_min());
+  T t2 = std::nextafter(T(), T());
+  VERIFY(t2 == T() && !std::signbit(t2));
+  T t3 = std::nextafter(lim::denorm_min(), T(-2.0));
+  VERIFY(t3 == T() && !std::signbit(t3));
+  T t4 = std::nextafter(lim::min(), T(-0.0));
+  VERIFY(std::fpclassify(t4) == FP_SUBNORMAL && t4 > T());
+  T t5 = std::nextafter(t4, T(1.0));
+  VERIFY(t5 == lim::min());
+  T t6 = std::nextafter(lim::min(), lim::infinity());
+  VERIFY(std::fpclassify(t6) == FP_NORMAL && t6 > lim::min());
+  T t7 = std::nextafter(t6, -lim::infinity());
+  VERIFY(t7 == lim::min());
+  T t8 = std::nextafter(T(16.0), T(16.5));
+  VERIFY(t8 > t7);
+  T t9 = std::nextafter(t8, T(15.5));
+  VERIFY(t9 == T(16.0));
+  T t10 = std::nextafter(lim::max(), T(-0.5));
+  VERIFY(std::fpclassify(t10) == FP_NORMAL && t10 < lim::max());
+  T t11 = std::nextafter(t10, lim::infinity());
+  VERIFY(t11 == lim::max());
+  T t12 = std::nextafter(t11, lim::infinity());
+  VERIFY(std::fpclassify(t12) == FP_INFINITE && !std::signbit(t12));
+  T t13 = std::nextafter(lim::infinity(), t12);
+  VERIFY(t13 == t12);
+  T t14 = std::nextafter(t13, T(1.0));
+  VERIFY(t14 == lim::max());
+  T t15 = std::nextafter(lim::quiet_NaN(), T());
+  VERIFY(std::fpclassify(t15) == FP_NAN);
+  T t16 = std::nextafter(T(17.0), lim::quiet_NaN());
+  VERIFY(std::fpclassify(t16) == FP_NAN);
+  T t17 = std::nextafter(T(), T(-0.0));
+  VERIFY(t17 == T() && std::signbit(t17));
+  T t20 = std::nextafter(T(-0.0), T(-2.0));
+  VERIFY(t20 == -lim::denorm_min());
+  T t21 = std::nextafter(T(), T(-1.0));
+  VERIFY(t21 == -lim::denorm_min());
+  T t22 = std::nextafter(T(-0.0), T(-0.0));
+  VERIFY(t22 == T() && std::signbit(t22));
+  T t23 = std::nextafter(-lim::denorm_min(), T(2.0));
+  VERIFY(t23 == T() && std::signbit(t23));
+  T t24 = std::nextafter(-lim::min(), T());
+  VERIFY(std::fpclassify(t24) == FP_SUBNORMAL && t24 < T());
+  T t25 = std::nextafter(t24, T(-1.0));
+  VERIFY(t25 == -lim::min());
+  T t26 = std::nextafter(-lim::min(), -lim::infinity());
+  VERIFY(std::fpclassify(t26) == FP_NORMAL && t26 < -lim::min());
+  T t27 = std::nextafter(t26, lim::infinity());
+  VERIFY(t27 == -lim::min());
+  T t28 = std::nextafter(T(-16.0), T(-16.5));
+  VERIFY(t28 < t27);
+  T t29 = std::nextafter(t28, T(-15.5));
+  VERIFY(t29 == T(-16.0));
+  T t30 = std::nextafter(-lim::max(), T(0.5));
+  VERIFY(std::fpclassify(t30) == FP_NORMAL && t30 > -lim::max());
+  T t31 = std::nextafter(t30, -lim::infinity());
+  VERIFY(t31 == -lim::max());
+  T t32 = std::nextafter(t31, -lim::infinity());
+  VERIFY(std::fpclassify(t32) == FP_INFINITE && std::signbit(t32));
+  T t33 = std::nextafter(-lim::infinity(), t32);
+  VERIFY(t33 == t32);
+  T t34 = std::nextafter(t33, T(-1.0));
+  VERIFY(t34 == -lim::max());
+  T t35 = std::nextafter(-lim::quiet_NaN(), T());
+  VERIFY(std::fpclassify(t35) == FP_NAN);
+  T t36 = std::nextafter(T(-17.0), lim::quiet_NaN());
+  VERIFY(std::fpclassify(t36) == FP_NAN);
+  T t37 = std::nextafter(T(-0.0), T());
+  VERIFY(t37 == T() && !std::signbit(t37));
+}
+
+int
+main ()
+{
+#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  test <std::float16_t>();
+#endif
+#if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  test <std::float32_t>();
+#endif
+#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
+  test <std::float64_t>();
+#endif
+#if defined(__STDCPP_FLOAT128_T__) \
+    && (defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY128) \
+	|| defined(_GLIBCXX_HAVE_FLOAT128_MATH))
+  test <std::float128_t>();
+#endif
+#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
+  test <std::bfloat16_t>();
+#endif
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-19  9:26 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-19  9:26 [gcc r13-3372] libstdc++-v3: Implement {, b}float16_t nextafter and some fixes [PR106652] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).