[libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to `std::fmax()` and `std::fmin()` (PR #222953)

Atharva Potdar via libcxx-commits libcxx-commits at lists.llvm.org
Fri Sep 11 08:51:47 PDT 2026


https://github.com/atharva-potdar updated https://github.com/llvm/llvm-project/pull/222953

>From 95c00402299c5a81f8c3d99363300b20245e97bc Mon Sep 17 00:00:00 2001
From: atharva-potdar <atharvapotdar07 at gmail.com>
Date: Fri, 11 Sep 2026 19:21:07 +0530
Subject: [PATCH] add constexpr for fmax() and fmin()

---
 libcxx/include/__math/min_max.h               | 16 ++--
 libcxx/include/cmath                          | 12 +--
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 22 +++---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       |  8 +-
 libcxx/test/std/numerics/c.math/fmax.pass.cpp | 75 +++++++++++++++++++
 libcxx/test/std/numerics/c.math/fmin.pass.cpp | 75 +++++++++++++++++++
 6 files changed, 179 insertions(+), 29 deletions(-)
 create mode 100644 libcxx/test/std/numerics/c.math/fmax.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/fmin.pass.cpp

diff --git a/libcxx/include/__math/min_max.h b/libcxx/include/__math/min_max.h
index 1ddbb557d1e8f..c4257fea5dcc3 100644
--- a/libcxx/include/__math/min_max.h
+++ b/libcxx/include/__math/min_max.h
@@ -25,21 +25,21 @@ namespace __math {
 
 // fmax
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI float fmax(float __x, float __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 float fmax(float __x, float __y) _NOEXCEPT {
   return __builtin_fmaxf(__x, __y);
 }
 
 template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI double fmax(double __x, double __y) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 double fmax(double __x, double __y) _NOEXCEPT {
   return __builtin_fmax(__x, __y);
 }
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI long double fmax(long double __x, long double __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 long double fmax(long double __x, long double __y) _NOEXCEPT {
   return __builtin_fmaxl(__x, __y);
 }
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __promote_t<_A1, _A2> fmax(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __promote_t<_A1, _A2> fmax(_A1 __x, _A2 __y) _NOEXCEPT {
   using __result_type = __promote_t<_A1, _A2>;
   static_assert(!(_IsSame<_A1, __result_type>::value && _IsSame<_A2, __result_type>::value), "");
   return __math::fmax((__result_type)__x, (__result_type)__y);
@@ -47,21 +47,21 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 
 // fmin
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI float fmin(float __x, float __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 float fmin(float __x, float __y) _NOEXCEPT {
   return __builtin_fminf(__x, __y);
 }
 
 template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI double fmin(double __x, double __y) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 double fmin(double __x, double __y) _NOEXCEPT {
   return __builtin_fmin(__x, __y);
 }
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI long double fmin(long double __x, long double __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 long double fmin(long double __x, long double __y) _NOEXCEPT {
   return __builtin_fminl(__x, __y);
 }
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI __promote_t<_A1, _A2> fmin(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __promote_t<_A1, _A2> fmin(_A1 __x, _A2 __y) _NOEXCEPT {
   using __result_type = __promote_t<_A1, _A2>;
   static_assert(!(_IsSame<_A1, __result_type>::value && _IsSame<_A2, __result_type>::value), "");
   return __math::fmin((__result_type)__x, (__result_type)__y);
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index c4f4030c2049b..e1289180a379d 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -196,13 +196,13 @@ floating_point fma (arithmetic x, arithmetic y, arithmetic z);
 float          fmaf(float x, float y, float z);
 long double    fmal(long double x, long double y, long double z);
 
-floating_point fmax (arithmetic x, arithmetic y);
-float          fmaxf(float x, float y);
-long double    fmaxl(long double x, long double y);
+floating_point fmax (arithmetic x, arithmetic y);   // constexpr since C++23
+float          fmaxf(float x, float y);             // constexpr since C++23
+long double    fmaxl(long double x, long double y); // constexpr since C++23
 
-floating_point fmin (arithmetic x, arithmetic y);
-float          fminf(float x, float y);
-long double    fminl(long double x, long double y);
+floating_point fmin (arithmetic x, arithmetic y);   // constexpr since C++23
+float          fminf(float x, float y);             // constexpr since C++23
+long double    fminl(long double x, long double y); // constexpr since C++23
 
 double         hermite(unsigned n, double x);                    // C++17
 float          hermite(unsigned n, float x);                     // C++17
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index beca4907284ad..95c21eb144bf4 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -185,17 +185,17 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fdimf(1.0f, 0.0f) == 1.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fdiml(1.0L, 0.0L) == 1.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmax(1.0f, 0.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmax(1.0, 0.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmax(1.0L, 0.0L) == 1.0L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmaxf(1.0f, 0.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmaxl(1.0L, 0.0L) == 1.0L);
-
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmin(1.0f, 0.0f) == 0.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmin(1.0, 0.0) == 0.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmin(1.0L, 0.0L) == 0.0L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fminf(1.0f, 0.0f) == 0.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fminl(1.0L, 0.0L) == 0.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fmax(1.0f, 0.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fmax(1.0, 0.0) == 1.0);
+  ASSERT_CONSTEXPR_CXX23(std::fmax(1.0L, 0.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fmaxf(1.0f, 0.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fmaxl(1.0L, 0.0L) == 1.0L);
+
+  ASSERT_CONSTEXPR_CXX23(std::fmin(1.0f, 0.0f) == 0.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fmin(1.0, 0.0) == 0.0);
+  ASSERT_CONSTEXPR_CXX23(std::fmin(1.0L, 0.0L) == 0.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fminf(1.0f, 0.0f) == 0.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fminl(1.0L, 0.0L) == 0.0L);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::fma(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fma(1.0, 1.0, 1.0) == 2.0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 0ae3f5802e21f..b444d39a7a6cc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -179,15 +179,15 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fdimf(1.0f, 0.0f) == 1.0f);
   ASSERT_CONSTEXPR_CXX23(std::fdiml(1.0L, 0.0L) == 1.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmax(1.0f, 0.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fmax(1.0f, 0.0f) == 1.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmax(1.0, 0.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmax(1.0L, 0.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fmax(1.0L, 0.0L) == 1.0L);
   ASSERT_CONSTEXPR_CXX23(std::fmaxf(1.0f, 0.0f) == 1.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmaxl(1.0L, 0.0L) == 1.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmin(1.0f, 0.0f) == 0.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fmin(1.0f, 0.0f) == 0.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmin(1.0, 0.0) == 0.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fmin(1.0L, 0.0L) == 0.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fmin(1.0L, 0.0L) == 0.0L);
   ASSERT_CONSTEXPR_CXX23(std::fminf(1.0f, 0.0f) == 0.0f);
   ASSERT_CONSTEXPR_CXX23(std::fminl(1.0L, 0.0L) == 0.0L);
 
diff --git a/libcxx/test/std/numerics/c.math/fmax.pass.cpp b/libcxx/test/std/numerics/c.math/fmax.pass.cpp
new file mode 100644
index 0000000000000..590e40f448e69
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fmax.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// floating-point-type fmax(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T inf = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
+
+    assert(std::fmax(T(1), T(2)) == T(2));
+    assert(std::fmax(T(2), T(1)) == T(2));
+    assert(std::fmax(T(-1), T(0)) == T(0));
+
+    assert(std::fmax(inf, T(1)) == inf);
+    assert(std::fmax(T(1), inf) == inf);
+    assert(std::fmax(-inf, T(1)) == T(1));
+    assert(std::fmax(T(1), -inf) == T(1));
+
+    assert(std::fmax(nan, T(1)) == T(1));
+    assert(std::fmax(T(1), nan) == T(1));
+    assert(std::fmax(s_nan, T(1)) == T(1));
+
+    assert(std::isnan(std::fmax(nan, nan)));
+  }
+};
+
+struct TestInt {
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
+    assert(std::fmax(T(0), T(1)) == T(1));
+    assert(std::fmax(T(1), T(0)) == T(1));
+    assert(std::fmax(low, max) == max); 
+
+    if (std::is_signed<T>::value) {
+      assert(std::fmax(T(-1), T(0)) == T(0));
+      assert(std::fmax(low, T(0)) == T(0));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX23 bool test() {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/fmin.pass.cpp b/libcxx/test/std/numerics/c.math/fmin.pass.cpp
new file mode 100644
index 0000000000000..a2c41bfa94021
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fmin.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// floating-point-type fmin(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T inf = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
+
+    assert(std::fmin(T(1), T(2)) == T(1));
+    assert(std::fmin(T(2), T(1)) == T(1));
+    assert(std::fmin(T(-1), T(0)) == T(-1));
+
+    assert(std::fmin(inf, T(1)) == T(1));
+    assert(std::fmin(T(1), inf) == T(1));
+    assert(std::fmin(-inf, T(1)) == -inf);
+    assert(std::fmin(T(1), -inf) == -inf);
+
+    assert(std::fmin(nan, T(1)) == T(1));
+    assert(std::fmin(T(1), nan) == T(1));
+    assert(std::fmin(s_nan, T(1)) == T(1));
+
+    assert(std::isnan(std::fmin(nan, nan)));
+  }
+};
+
+struct TestInt {
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
+    assert(std::fmin(T(0), T(1)) == T(0));
+    assert(std::fmin(T(1), T(0)) == T(0));
+    assert(std::fmin(low, max) == low); 
+
+    if (std::is_signed<T>::value) {
+      assert(std::fmin(T(-1), T(0)) == T(-1));
+      assert(std::fmin(low, T(0)) == T(low));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX23 bool test() {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+  return 0;
+}



More information about the libcxx-commits mailing list