[libcxx-commits] [libcxx] [libcxx] p3008 atomic fp min/max (PR #186716)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Mar 15 16:25:10 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: None (gonzalobg)

<details>
<summary>Changes</summary>

Closes #<!-- -->148168 .
Blocked by #<!-- -->86694 since this will need to bump the version macro further.

---

Patch is 91.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/186716.diff


29 Files Affected:

- (modified) libcxx/docs/ReleaseNotes/23.rst (+1) 
- (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1) 
- (modified) libcxx/include/__atomic/atomic.h (+109-3) 
- (modified) libcxx/include/__atomic/atomic_ref.h (+58) 
- (modified) libcxx/include/__math/min_max.h (+193) 
- (modified) libcxx/include/cmath (+17) 
- (modified) libcxx/modules/std/cmath.inc (+5) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fmax.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fmaximum.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fmaximum_num.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fmin.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fminimum.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.ref/fetch_fminimum_num.pass.cpp (+65) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fmax.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fmaximum.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fmaximum_num.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fmin.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fminimum.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_fminimum_num.pass.cpp (+54) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fmax_helper.h (+112) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fmaximum_helper.h (+203) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fmaximum_num_helper.h (+203) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fmin_helper.h (+112) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fminimum_helper.h (+203) 
- (added) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_fminimum_num_helper.h (+203) 
- (added) libcxx/test/std/numerics/c.math/fmaximum.pass.cpp (+86) 
- (added) libcxx/test/std/numerics/c.math/fmaximum_num.pass.cpp (+86) 
- (added) libcxx/test/std/numerics/c.math/fminimum.pass.cpp (+86) 
- (added) libcxx/test/std/numerics/c.math/fminimum_num.pass.cpp (+86) 


``````````diff
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 4441a8aed198c..76223c4e0dccb 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P3008R6: Atomic floating-point min/max (`Github <https://github.com/llvm/llvm-project/issues/148168>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..697eb8aff503e 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -147,7 +147,7 @@
 "`P3557R3 <https://wg21.link/P3557R3>`__","High-Quality Sender Diagnostics with Constexpr Exceptions","2025-06 (Sofia)","","","`#148162 <https://github.com/llvm/llvm-project/issues/148162>`__",""
 "`P3560R2 <https://wg21.link/P3560R2>`__","Error Handling in Reflection","2025-06 (Sofia)","","","`#148128 <https://github.com/llvm/llvm-project/issues/148128>`__",""
 "`P3503R3 <https://wg21.link/P3503R3>`__","Make type-erased allocator use in ``promise`` and ``packaged_task`` consistent","2025-06 (Sofia)","","","`#148164 <https://github.com/llvm/llvm-project/issues/148164>`__",""
-"`P3008R6 <https://wg21.link/P3008R6>`__","Atomic floating-point min/max","2025-06 (Sofia)","","","`#148168 <https://github.com/llvm/llvm-project/issues/148168>`__",""
+"`P3008R6 <https://wg21.link/P3008R6>`__","Atomic floating-point min/max","2025-06 (Sofia)","|Complete|","23","`#148168 <https://github.com/llvm/llvm-project/issues/148168>`__",""
 "`P3111R8 <https://wg21.link/P3111R8>`__","Atomic Reduction Operations","2025-06 (Sofia)","","","`#148174 <https://github.com/llvm/llvm-project/issues/148174>`__",""
 "`P3060R3 <https://wg21.link/P3060R3>`__","Add ``std::views::indices(n)``","2025-06 (Sofia)","|Complete|","22","`#148175 <https://github.com/llvm/llvm-project/issues/148175>`__",""
 "`P2319R5 <https://wg21.link/P2319R5>`__","Prevent ``path`` presentation problems","2025-06 (Sofia)","","","`#148177 <https://github.com/llvm/llvm-project/issues/148177>`__",""
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 3d0ed9cc47396..23351d0ba8f9d 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -18,6 +18,7 @@
 #include <__atomic/support.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
+#include <__math/min_max.h>
 #include <__memory/addressof.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_floating_point.h>
@@ -331,10 +332,15 @@ template <class _Tp>
   requires is_floating_point_v<_Tp>
 struct atomic<_Tp> : __atomic_base<_Tp> {
 private:
-  template <class _This, class _Operation, class _BuiltinOp>
+  template <class _This, class _Operation, class _BuiltinOp, bool _HasBuiltin = std::__has_rmw_builtin<_Tp>()>
   _LIBCPP_HIDE_FROM_ABI static _Tp
-  __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _Operation __operation, _BuiltinOp __builtin_op) {
-    if constexpr (std::__has_rmw_builtin<_Tp>()) {
+  __rmw_op(_This&& __self,
+           _Tp __operand,
+           memory_order __m,
+           _Operation __operation,
+           _BuiltinOp __builtin_op,
+           std::integral_constant<bool, _HasBuiltin> = {}) {
+    if constexpr (_HasBuiltin) {
       return __builtin_op(std::addressof(std::forward<_This>(__self).__a_), __operand, __m);
     } else {
       _Tp __old = __self.load(memory_order_relaxed);
@@ -372,6 +378,44 @@ struct atomic<_Tp> : __atomic_base<_Tp> {
     return __rmw_op(std::forward<_This>(__self), __operand, __m, __minus, __builtin_op);
   }
 
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_min(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fmin(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_max(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fmax(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+
+#  if _LIBCPP_STD_VER >= 26
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_fminimum(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fminimum(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_fmaximum(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fmaximum(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_fminimum_num(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fminimum_num(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+
+  template <class _This>
+  _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_fmaximum_num(_This&& __self, _Tp __operand, memory_order __m) {
+    auto __op = [](_Tp __a, _Tp __b) { return std::__math::fmaximum_num(__a, __b); };
+    return __rmw_op(std::forward<_This>(__self), __operand, __m, __op, __op, std::false_type{});
+  }
+#  endif // _LIBCPP_STD_VER >= 26
+
 public:
   using __base _LIBCPP_NODEBUG = __atomic_base<_Tp>;
   using value_type             = _Tp;
@@ -415,6 +459,68 @@ struct atomic<_Tp> : __atomic_base<_Tp> {
     return __fetch_sub(*this, __op, __m);
   }
 
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_min(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_min(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_max(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_max(*this, __op, __m);
+  }
+
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_fminimum(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_fminimum(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_fmaximum(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_fmaximum(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum_num(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_fminimum_num(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum_num(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_fminimum_num(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum_num(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+    requires __base::is_always_lock_free
+  {
+    return __fetch_fmaximum_num(*this, __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum_num(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+    return __fetch_fmaximum_num(*this, __op, __m);
+  }
+#  endif // _LIBCPP_STD_VER >= 26
+
   _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile noexcept
     requires __base::is_always_lock_free
   {
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 69edbfe6ecadd..fe38048cced1a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -28,9 +28,11 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/byte.h>
+#include <__math/min_max.h>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <cstdint>
 #include <cstring>
@@ -355,6 +357,62 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
     }
   }
 
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fmin(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fmin(__old, __arg);
+    }
+    return __old;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fmax(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fmax(__old, __arg);
+    }
+    return __old;
+  }
+
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fminimum(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fminimum(__old, __arg);
+    }
+    return __old;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fmaximum(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fmaximum(__old, __arg);
+    }
+    return __old;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fminimum_num(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fminimum_num(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fminimum_num(__old, __arg);
+    }
+    return __old;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_fmaximum_num(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new = std::__math::fmaximum_num(__old, __arg);
+    while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
+      __new = std::__math::fmaximum_num(__old, __arg);
+    }
+    return __old;
+  }
+#  endif // _LIBCPP_STD_VER >= 26
+
   _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
   _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
 };
diff --git a/libcxx/include/__math/min_max.h b/libcxx/include/__math/min_max.h
index 1ddbb557d1e8f..deb4a9b1563e8 100644
--- a/libcxx/include/__math/min_max.h
+++ b/libcxx/include/__math/min_max.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_arithmetic.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/promote.h>
 
@@ -67,6 +68,198 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
   return __math::fmin((__result_type)__x, (__result_type)__y);
 }
 
+// fminimum (IEEE 754-2019 minimum)
+
+// Fallback implementation for all floating-point types
+template <typename _Tp>
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __fminimum_fallback(_Tp __x, _Tp __y) _NOEXCEPT {
+  // Handle NaN: propagate NaN
+  if (__builtin_isnan(__x)) return __x;
+  if (__builtin_isnan(__y)) return __y;
+
+  // Handle signed zeros: -0.0 < +0.0
+  if (__x == _Tp(0) && __y == _Tp(0)) {
+    const bool __x_is_neg = __builtin_signbit(__x);
+    const bool __y_is_neg = __builtin_signbit(__y);
+    if (__x_is_neg != __y_is_neg)
+      return __x_is_neg ? __x : __y;
+  }
+
+  // Regular comparison
+  return __x < __y ? __x : __y;
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr float fminimum(float __x, float __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimumf) // TODO
+  return __builtin_fminimumf(__x, __y);
+#else
+  return __fminimum_fallback(__x, __y);
+#endif
+}
+
+template <class = int>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr double fminimum(double __x, double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimum) // TODO
+  return __builtin_fminimum(__x, __y);
+#else
+  return __fminimum_fallback(__x, __y);
+#endif
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr long double fminimum(long double __x, long double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimuml) // TODO
+  return __builtin_fminimuml(__x, __y);
+#else
+  return __fminimum_fallback(__x, __y);
+#endif
+}
+
+// fminimum_num (IEEE 754-2019 minimumNumber)
+
+// Fallback implementation for all floating-point types
+template <typename _Tp>
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __fminimum_num_fallback(_Tp __x, _Tp __y) _NOEXCEPT {
+  // Handle NaN: favor non-NaN values
+  const bool __x_is_nan = __builtin_isnan(__x);
+  const bool __y_is_nan = __builtin_isnan(__y);
+  if (__x_is_nan)
+    return __y_is_nan ? __x : __y;
+  if (__y_is_nan)
+    return __x;
+
+  // Handle signed zeros: -0.0 < +0.0
+  if (__x == _Tp(0) && __y == _Tp(0)) {
+    const bool __x_is_neg = __builtin_signbit(__x);
+    const bool __y_is_neg = __builtin_signbit(__y);
+    if (__x_is_neg != __y_is_neg)
+      return __x_is_neg ? __x : __y;
+  }
+
+  // Regular comparison
+  return __x < __y ? __x : __y;
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr float fminimum_num(float __x, float __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimum_numf) // TODO
+  return __builtin_fminimum_numf(__x, __y);
+#else
+  return __fminimum_num_fallback(__x, __y);
+#endif
+}
+
+template <class = int>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr double fminimum_num(double __x, double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimum_num) // TODO
+  return __builtin_fminimum_num(__x, __y);
+#else
+  return __fminimum_num_fallback(__x, __y);
+#endif
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr long double fminimum_num(long double __x, long double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fminimum_numl) // TODO
+  return __builtin_fminimum_numl(__x, __y);
+#else
+  return __fminimum_num_fallback(__x, __y);
+#endif
+}
+
+// fmaximum (IEEE 754-2019 maximum)
+
+// Fallback implementation for all floating-point types
+template <typename _Tp>
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __fmaximum_fallback(_Tp __x, _Tp __y) _NOEXCEPT {
+  // Handle NaN: propagate NaN
+  if (__builtin_isnan(__x)) return __x;
+  if (__builtin_isnan(__y)) return __y;
+
+  // Handle signed zeros: -0.0 < +0.0, so max returns +0.0
+  if (__x == _Tp(0) && __y == _Tp(0)) {
+    const bool __x_is_neg = __builtin_signbit(__x);
+    const bool __y_is_neg = __builtin_signbit(__y);
+    if (__x_is_neg != __y_is_neg)
+      return __x_is_neg ? __y : __x;  // Return the positive zero
+  }
+
+  // Regular comparison
+  return __x > __y ? __x : __y;
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr float fmaximum(float __x, float __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximumf) // TODO
+  return __builtin_fmaximumf(__x, __y);
+#else
+  return __fmaximum_fallback(__x, __y);
+#endif
+}
+
+template <class = int>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr double fmaximum(double __x, double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximum) // TODO
+  return __builtin_fmaximum(__x, __y);
+#else
+  return __fmaximum_fallback(__x, __y);
+#endif
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr long double fmaximum(long double __x, long double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximuml) // TODO
+  return __builtin_fmaximuml(__x, __y);
+#else
+  return __fmaximum_fallback(__x, __y);
+#endif
+}
+
+// fmaximum_num (IEEE 754-2019 maximumNumber)
+
+// Fallback implementation for all floating-point types
+template <typename _Tp>
+inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __fmaximum_num_fallback(_Tp __x, _Tp __y) _NOEXCEPT {
+  // Handle NaN: favor non-NaN values
+  const bool __x_is_nan = __builtin_isnan(__x);
+  const bool __y_is_nan = __builtin_isnan(__y);
+  if (__x_is_nan)
+    return __y_is_nan ? __x : __y;
+  if (__y_is_nan)
+    return __x;
+
+  // Handle signed zeros: -0.0 < +0.0, so max returns +0.0
+  if (__x == _Tp(0) && __y == _Tp(0)) {
+    const bool __x_is_neg = __builtin_signbit(__x);
+    const bool __y_is_neg = __builtin_signbit(__y);
+    if (__x_is_neg != __y_is_neg)
+      return __x_is_neg ? __y : __x;  // Return the positive zero
+  }
+
+  // Regular comparison
+  return __x > __y ? __x : __y;
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr float fmaximum_num(float __x, float __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximum_numf) // TODO
+  return __builtin_fmaximum_numf(__x, __y);
+#else
+  return __fmaximum_num_fallback(__x, __y);
+#endif
+}
+
+template <class = int>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr double fmaximum_num(double __x, double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximum_num) // TODO
+  return __builtin_fmaximum_num(__x, __y);
+#else
+  return __fmaximum_num_fallback(__x, __y);
+#endif
+}
+
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI constexpr long double fmaximum_num(long double __x, long double __y) _NOEXCEPT {
+#if 0 // __has_builtin(__builtin_fmaximum_numl) // TODO
+  return __builtin_fmaximum_numl(__x, __y);
+#else
+  return __fmaximum_num_fallback(__x, __y);
+#endif
+}
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index bee743f702d01..a45ed9d185fc8 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -204,6 +204,14 @@ floating_point fmin (arithmetic x, arithmetic y);
 float          fminf(float x, float y);
 long double    fminl(long double x, long double y);
 
+constexpr float       fminimum_num(float x, float y);            // C++26 (from C23)
+constexpr double      fminimum_num(double x, double y);          // C++26 (from C23)
+constexpr long double fminimum_num(long double x, long double y); // C++26 (from C23)
+
+constexpr float       fmaximum_num(float x, float y);            // C++26 (from C23)
+constexpr double      fmaximum_num(double x, double y);          // C++26 (from C23)
+constexpr long double fmaximum_num(long double x, long double y); // C++26 (from C23)
+
 double         hermite(unsigned n, double x);                    // C++17
 float          hermite(unsigned n, float x);                     // C++17
 long double    hermite(unsigned n, long double x);               // C++17
@@ -317,6 +325,7 @@ constexpr long double lerp(long double a, long double b, long double t) noexcept
 #else
 #  include <__config>
 #  include <__math/hypot.h>
+#  include <__math/min_max.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_arithmetic.h>
 #  include <__type_traits/is_constant_evaluated.h>
@@ -608,6 +617,14 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr __promote_t<_A1, _A2, _A3> lerp(_A1 __a,
 }
 #  endif // _LIBCPP_STD_VER >= 20
 
+#  if _LIBCPP_STD_VER >= 26
+// C++26 IEEE 754-2019 min/max functions (from C23)
+using __math::fminimum;
+using __math::fmaximum;
+using __math::fminimum_num;
+using __math::...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/186716


More information about the libcxx-commits mailing list