[libcxx-commits] [libcxx] [libc++][math] Add constexpr for std::abs() and relatives (PR #202985)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jun 10 07:03:49 PDT 2026


https://github.com/pixafera created https://github.com/llvm/llvm-project/pull/202985

Mark `std::abs`, `std::fabs`, `std::labs` and `std::llabs` as constexpr.

Refs [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf), #105174 

Following on the convention established in #98952 for `std::isnan` and `std::isinf`, I used `_LIBCPP_PREFERRED_OVERLOAD` on the overloads that are ambiguous to the underlying C library so that the constexpr C++ functions are now preferred over the C library ones.

>From eff3d54be665f9278978c8f4b9f9a3d725571ef5 Mon Sep 17 00:00:00 2001
From: pixafera <npierce25 at bloomberg.net>
Date: Wed, 10 Jun 2026 14:36:21 +0100
Subject: [PATCH] Make abs and relatives constexpr

---
 libcxx/include/__math/abs.h                   | 84 +++++++++++++++----
 libcxx/include/math.h                         |  2 +
 libcxx/include/stdlib.h                       |  2 +
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 26 +++---
 libcxx/test/std/numerics/c.math/abs.pass.cpp  | 36 ++++++++
 5 files changed, 123 insertions(+), 27 deletions(-)

diff --git a/libcxx/include/__math/abs.h b/libcxx/include/__math/abs.h
index b780159f11ebf..74fc55281916c 100644
--- a/libcxx/include/__math/abs.h
+++ b/libcxx/include/__math/abs.h
@@ -23,43 +23,99 @@ namespace __math {
 
 // fabs
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI float fabs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 inline _LIBCPP_HIDE_FROM_ABI float fabs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
 
-template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI double fabs(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    double
+    fabs(double __x) _NOEXCEPT {
   return __builtin_fabs(__x);
 }
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI long double fabs(long double __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 inline _LIBCPP_HIDE_FROM_ABI long double fabs(long double __x) _NOEXCEPT {
   return __builtin_fabsl(__x);
 }
 
 template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI double fabs(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 inline _LIBCPP_HIDE_FROM_ABI double fabs(_A1 __x) _NOEXCEPT {
   return __builtin_fabs((double)__x);
 }
 
+// fabsf
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    float
+    fabsf(float __x) _NOEXCEPT {
+  return __builtin_fabsf(__x);
+}
+
+// fabsl
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    long double
+    fabsl(long double __x) _NOEXCEPT {
+  return __builtin_fabsl(__x);
+}
+
 // abs
 
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT { return __builtin_fabs(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline float abs(float __x) _NOEXCEPT { return __builtin_fabsf(__x); }
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline double abs(double __x) _NOEXCEPT { return __builtin_fabs(__x); }
 
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long double abs(long double __x) _NOEXCEPT {
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI inline long double abs(long double __x) _NOEXCEPT {
   return __builtin_fabsl(__x);
 }
 
-template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline int abs(int __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    int
+    abs(int __x) _NOEXCEPT {
   return __builtin_abs(__x);
 }
 
-template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long abs(long __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    long
+    abs(long __x) _NOEXCEPT {
+  return __builtin_labs(__x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    long long
+    abs(long long __x) _NOEXCEPT {
+  return __builtin_llabs(__x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    long
+    labs(long __x) _NOEXCEPT {
   return __builtin_labs(__x);
 }
 
-template <class = int>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI inline long long abs(long long __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+    long long
+    llabs(long long __x) _NOEXCEPT {
   return __builtin_llabs(__x);
 }
 
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..23a83e3dcca2e 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -472,6 +472,8 @@ using std::__math::exp;
 using std::__math::exp2;
 using std::__math::expm1;
 using std::__math::fabs;
+using std::__math::fabsf;
+using std::__math::fabsl;
 using std::__math::fdim;
 using std::__math::floor;
 using std::__math::fma;
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..67d667aab2453 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -108,6 +108,8 @@ extern "C++" {
 
 #      include <__math/abs.h>
 using std::__math::abs;
+using std::__math::labs;
+using std::__math::llabs;
 
 // div
 
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 93865afc5e68b..e9d3604a5e756 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
@@ -43,15 +43,15 @@ int main(int, char**) {
   double DummyDouble;
   long double DummyLongDouble;
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1L) == 1L);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1LL) == 1LL);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0) == 1.0);
+  ASSERT_CONSTEXPR_CXX23(std::abs(-1.0L) == 1.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::labs(-1L) == 1L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::llabs(-1LL) == 1LL);
+  ASSERT_CONSTEXPR_CXX23(std::labs(-1L) == 1L);
+  ASSERT_CONSTEXPR_CXX23(std::llabs(-1LL) == 1LL);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::div(13, 5).rem == 3);
   ASSERT_NOT_CONSTEXPR_CXX23(std::div(13L, 5L).rem == 3L);
@@ -101,11 +101,11 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::scalblnf(1.0f, 1L) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::scalblnl(1.0L, 1L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fabs(-1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fabs(-1.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fabs(-1.0L) == 1.0L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fabsf(-1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fabsl(-1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fabs(-1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fabs(-1.0) == 1.0);
+  ASSERT_CONSTEXPR_CXX23(std::fabs(-1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::fabsf(-1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::fabsl(-1.0L) == 1.0L);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::ceil(0.0f) == 0.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::ceil(0.0) == 0.0);
diff --git a/libcxx/test/std/numerics/c.math/abs.pass.cpp b/libcxx/test/std/numerics/c.math/abs.pass.cpp
index 51aee6e986836..482f249dbdcd8 100644
--- a/libcxx/test/std/numerics/c.math/abs.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/abs.pass.cpp
@@ -37,6 +37,19 @@ void test_big() {
   assert(std::abs(negative_big_value) == big_value); // make sure it doesn't get casted to a smaller type
 }
 
+template <class Source, class Result>
+constexpr bool test_constexpr_abs() {
+  Source neg_val = -5;
+  Source pos_val = 5;
+  Result res = 5;
+
+  ASSERT_SAME_TYPE(decltype(std::abs(neg_val)), Result);
+
+  assert(std::abs(neg_val) == res);
+  assert(std::abs(pos_val) == res);
+  return true;
+}
+
 // The following is helpful to keep in mind:
 // 1byte == char <= short <= int <= long <= long long
 
@@ -68,5 +81,28 @@ int main(int, char**) {
 
   test_big();
 
+#if TEST_STD_VER > 23
+  // All types less than or equal to and not greater than int are promoted to int.
+  static_assert(test_constexpr_abs<short int, int>());
+  static_assert(test_constexpr_abs<SignedChar, int>());
+  static_assert(test_constexpr_abs<signed char, int>());
+
+  // These three calls have specific overloads:
+  static_assert(test_constexpr_abs<int, int>());
+  static_assert(test_constexpr_abs<long int, long int>());
+  static_assert(test_constexpr_abs<long long int, long long int>());
+
+  // Here there is no guarantee that int is larger than int8_t so we
+  // use a helper type trait to conditional test against int.
+  static_assert(test_constexpr_abs<std::int8_t, correct_size_int<std::int8_t>::type>());
+  static_assert(test_constexpr_abs<std::int16_t, correct_size_int<std::int16_t>::type>());
+  static_assert(test_constexpr_abs<std::int32_t, correct_size_int<std::int32_t>::type>());
+  static_assert(test_constexpr_abs<std::int64_t, correct_size_int<std::int64_t>::type>());
+
+  static_assert(test_constexpr_abs<long double, long double>());
+  static_assert(test_constexpr_abs<double, double>());
+  static_assert(test_constexpr_abs<float, float>());
+#endif
+
   return 0;
 }



More information about the libcxx-commits mailing list