[libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to `std::fpclassify` (PR #210993)

Lucas Mellone via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 24 06:18:47 PDT 2026


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/7] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
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..926ff37cddbfc 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
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/7] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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 d8779706bcee2..455c03ef58d18 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
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/7] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // 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>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/7] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/7] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    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::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  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::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+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;
 }

>From 5ff2b23ff45081ec8cdef90f220fbca3cdba9c90 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:17:12 +0200
Subject: [PATCH 6/7] remove: _LIBCPP_PREFERRED_OVERLOAD for int overload

---
 libcxx/include/math.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index fa8d293604c7f..9b14e57d7b262 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -402,13 +402,6 @@ fpclassify(double __x) _NOEXCEPT {
 fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
-
-template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
-fpclassify(_A1 __x) _NOEXCEPT {
-  return __x == 0 ? FP_ZERO : FP_NORMAL;
-}
-
 #      else
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
@@ -431,7 +424,6 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
-
 #      endif
 
 } // namespace __math

>From 742a100c4432c14549ef9e0cc6dd1dce6b40e316 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:18:06 +0200
Subject: [PATCH 7/7] remove trailing line

---
 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fcfd73d9bacf2..10b2297fce105 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -75,6 +75,5 @@ int main(int, char**) {
 #if TEST_STD_VER >= 23
   static_assert(test());
 #endif
-
   return 0;
 }



More information about the libcxx-commits mailing list