[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 07:05:13 PDT 2026
https://github.com/atharva-potdar created https://github.com/llvm/llvm-project/pull/222953
Adds `constexpr` to `std::fmax()` and `std::fmin()` as required by [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf).
Towards #105174.
>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