[libcxx-commits] [libcxx] WIP [libc++][math] Add `constexpr` to `copysign`, `copysignf`, `copysignl` (PR #216285)

Lucas Mellone via libcxx-commits libcxx-commits at lists.llvm.org
Thu Sep 17 06:47:57 PDT 2026


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

>From 714be78ad730429ed9591a77d040b9def148f0e7 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 14 Aug 2026 11:35:19 +0200
Subject: [PATCH 1/8] constexpr cxx23 copysign

---
 libcxx/include/__math/copysign.h              | 20 ++++++++++++++++---
 libcxx/include/cmath                          |  6 +++---
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 10 +++++-----
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__math/copysign.h b/libcxx/include/__math/copysign.h
index 4c297cb0894e9..ea67c8d8704e3 100644
--- a/libcxx/include/__math/copysign.h
+++ b/libcxx/include/__math/copysign.h
@@ -24,19 +24,33 @@ namespace __math {
 
 // copysign
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI float copysign(float __x, float __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI float
+copysign(float __x, float __y) _NOEXCEPT {
   return ::__builtin_copysignf(__x, __y);
 }
 
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI long double copysign(long double __x, long double __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI long double
+copysign(long double __x, long double __y) _NOEXCEPT {
   return ::__builtin_copysignl(__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> copysign(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __promote_t<_A1, _A2>
+copysign(_A1 __x, _A2 __y) _NOEXCEPT {
   return ::__builtin_copysign(__x, __y);
 }
 
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD float copysignf(float __x, float __y) _NOEXCEPT {
+  return __builtin_copysignf(__x, __y);
+}
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD long double
+copysignl(long double __x, long double __y) _NOEXCEPT {
+  return __builtin_copysignl(__x, __y);
+}
+#endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index c4f4030c2049b..8cc48912b46bb 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -424,8 +424,8 @@ using ::atanhf _LIBCPP_USING_IF_EXISTS;
 using ::cbrt _LIBCPP_USING_IF_EXISTS;
 using ::cbrtf _LIBCPP_USING_IF_EXISTS;
 
-using ::copysign _LIBCPP_USING_IF_EXISTS;
-using ::copysignf _LIBCPP_USING_IF_EXISTS;
+using std::__math::copysign _LIBCPP_USING_IF_EXISTS;
+using std::__math::copysignf _LIBCPP_USING_IF_EXISTS;
 
 using ::erf _LIBCPP_USING_IF_EXISTS;
 using ::erff _LIBCPP_USING_IF_EXISTS;
@@ -518,7 +518,7 @@ using ::asinhl _LIBCPP_USING_IF_EXISTS;
 using ::atanhl _LIBCPP_USING_IF_EXISTS;
 using ::cbrtl _LIBCPP_USING_IF_EXISTS;
 
-using ::copysignl _LIBCPP_USING_IF_EXISTS;
+using std::__math::copysignl _LIBCPP_USING_IF_EXISTS;
 
 using ::erfl _LIBCPP_USING_IF_EXISTS;
 using ::erfcl _LIBCPP_USING_IF_EXISTS;
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..3b8b418884d3d 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
@@ -161,11 +161,11 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::remquof(0.5f, 1.0f, &DummyInt) == 0.5f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::remquol(0.5L, 1.0L, &DummyInt) == 0.5L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysign(1.0f, 1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysign(1.0, 1.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysign(1.0L, 1.0L) == 1.0L);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysignf(1.0f, 1.0f) == 1.0f);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysignl(1.0L, 1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::copysign(1.0f, 1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::copysign(1.0, 1.0) == 1.0);
+  ASSERT_CONSTEXPR_CXX23(std::copysign(1.0L, 1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::copysignf(1.0f, 1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::copysignl(1.0L, 1.0L) == 1.0L);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::nextafter(0.0f, 0.0f) == 0.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::nextafter(0.0, 0.0) == 0.0);

>From 9b8bf93665db2c2e1bd1c2e973b2a4d24cf7984e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 15 Aug 2026 17:57:51 +0200
Subject: [PATCH 2/8] revert few changes

---
 libcxx/include/__math/copysign.h | 11 -----------
 libcxx/include/cmath             |  6 +++---
 2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/copysign.h b/libcxx/include/__math/copysign.h
index ea67c8d8704e3..7e3b5db2d9887 100644
--- a/libcxx/include/__math/copysign.h
+++ b/libcxx/include/__math/copysign.h
@@ -40,17 +40,6 @@ copysign(_A1 __x, _A2 __y) _NOEXCEPT {
   return ::__builtin_copysign(__x, __y);
 }
 
-#ifdef _LIBCPP_PREFERRED_OVERLOAD
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD float copysignf(float __x, float __y) _NOEXCEPT {
-  return __builtin_copysignf(__x, __y);
-}
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD long double
-copysignl(long double __x, long double __y) _NOEXCEPT {
-  return __builtin_copysignl(__x, __y);
-}
-#endif
-
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 8cc48912b46bb..c4f4030c2049b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -424,8 +424,8 @@ using ::atanhf _LIBCPP_USING_IF_EXISTS;
 using ::cbrt _LIBCPP_USING_IF_EXISTS;
 using ::cbrtf _LIBCPP_USING_IF_EXISTS;
 
-using std::__math::copysign _LIBCPP_USING_IF_EXISTS;
-using std::__math::copysignf _LIBCPP_USING_IF_EXISTS;
+using ::copysign _LIBCPP_USING_IF_EXISTS;
+using ::copysignf _LIBCPP_USING_IF_EXISTS;
 
 using ::erf _LIBCPP_USING_IF_EXISTS;
 using ::erff _LIBCPP_USING_IF_EXISTS;
@@ -518,7 +518,7 @@ using ::asinhl _LIBCPP_USING_IF_EXISTS;
 using ::atanhl _LIBCPP_USING_IF_EXISTS;
 using ::cbrtl _LIBCPP_USING_IF_EXISTS;
 
-using std::__math::copysignl _LIBCPP_USING_IF_EXISTS;
+using ::copysignl _LIBCPP_USING_IF_EXISTS;
 
 using ::erfl _LIBCPP_USING_IF_EXISTS;
 using ::erfcl _LIBCPP_USING_IF_EXISTS;

>From b3d9ed5dcbb701de1928eb093b4f380e444e69cb Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 17 Aug 2026 01:36:39 +0200
Subject: [PATCH 3/8] wip: namespace resolution

---
 libcxx/include/__math/copysign.h |  7 +++++--
 libcxx/include/cmath             | 15 +++++++++++++++
 libcxx/include/math.h            |  1 -
 3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/copysign.h b/libcxx/include/__math/copysign.h
index 7e3b5db2d9887..e87097ba85449 100644
--- a/libcxx/include/__math/copysign.h
+++ b/libcxx/include/__math/copysign.h
@@ -29,6 +29,9 @@ copysign(float __x, float __y) _NOEXCEPT {
   return ::__builtin_copysignf(__x, __y);
 }
 
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+double copysign(double __x, double __y) _NOEXCEPT { return __builtin_copysign(__x, __y); }
+
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI long double
 copysign(long double __x, long double __y) _NOEXCEPT {
   return ::__builtin_copysignl(__x, __y);
@@ -36,8 +39,8 @@ copysign(long double __x, long double __y) _NOEXCEPT {
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __promote_t<_A1, _A2>
-copysign(_A1 __x, _A2 __y) _NOEXCEPT {
-  return ::__builtin_copysign(__x, __y);
+    copysign(_A1 __x, _A2 __y) _NOEXCEPT {
+      return ::__builtin_copysign(__x, __y);
 }
 
 } // namespace __math
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index c4f4030c2049b..2ee9f078a843e 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -424,8 +424,17 @@ using ::atanhf _LIBCPP_USING_IF_EXISTS;
 using ::cbrt _LIBCPP_USING_IF_EXISTS;
 using ::cbrtf _LIBCPP_USING_IF_EXISTS;
 
+#if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysign)
+_LIBCPP_HIDE_FROM_ABI constexpr double copysign(double __x, double __y) _NOEXCEPT {
+  return __builtin_copysign(__x, __y);
+}
+_LIBCPP_HIDE_FROM_ABI constexpr float copysignf(float __x, float __y) _NOEXCEPT {
+  return __builtin_copysignf(__x, __y);
+}
+#else
 using ::copysign _LIBCPP_USING_IF_EXISTS;
 using ::copysignf _LIBCPP_USING_IF_EXISTS;
+#endif
 
 using ::erf _LIBCPP_USING_IF_EXISTS;
 using ::erff _LIBCPP_USING_IF_EXISTS;
@@ -518,7 +527,13 @@ using ::asinhl _LIBCPP_USING_IF_EXISTS;
 using ::atanhl _LIBCPP_USING_IF_EXISTS;
 using ::cbrtl _LIBCPP_USING_IF_EXISTS;
 
+#if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysignl)
+_LIBCPP_HIDE_FROM_ABI constexpr long double copysignl(long double __x, long double __y) _NOEXCEPT {
+  return __builtin_copysignl(__x, __y);
+}
+#else
 using ::copysignl _LIBCPP_USING_IF_EXISTS;
+#endif
 
 using ::erfl _LIBCPP_USING_IF_EXISTS;
 using ::erfcl _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 9d0642398b7d4..6f520634d0646 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -491,7 +491,6 @@ using std::__math::atan2;
 using std::__math::atanh;
 using std::__math::cbrt;
 using std::__math::ceil;
-using std::__math::copysign;
 using std::__math::cos;
 using std::__math::cosh;
 using std::__math::erf;

>From c3454b8f9e1cbc99b3ed03413d83f6afd386a870 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 17 Aug 2026 01:40:37 +0200
Subject: [PATCH 4/8] fix: clang-format

---
 libcxx/include/__math/copysign.h | 10 ++++++----
 libcxx/include/cmath             | 12 ++++++------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__math/copysign.h b/libcxx/include/__math/copysign.h
index e87097ba85449..c100c03650733 100644
--- a/libcxx/include/__math/copysign.h
+++ b/libcxx/include/__math/copysign.h
@@ -29,8 +29,10 @@ copysign(float __x, float __y) _NOEXCEPT {
   return ::__builtin_copysignf(__x, __y);
 }
 
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
-double copysign(double __x, double __y) _NOEXCEPT { return __builtin_copysign(__x, __y); }
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI double
+copysign(double __x, double __y) _NOEXCEPT {
+  return __builtin_copysign(__x, __y);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI long double
 copysign(long double __x, long double __y) _NOEXCEPT {
@@ -39,8 +41,8 @@ copysign(long double __x, long double __y) _NOEXCEPT {
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __promote_t<_A1, _A2>
-    copysign(_A1 __x, _A2 __y) _NOEXCEPT {
-      return ::__builtin_copysign(__x, __y);
+copysign(_A1 __x, _A2 __y) _NOEXCEPT {
+  return ::__builtin_copysign(__x, __y);
 }
 
 } // namespace __math
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 2ee9f078a843e..dd53594ea7380 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -424,17 +424,17 @@ using ::atanhf _LIBCPP_USING_IF_EXISTS;
 using ::cbrt _LIBCPP_USING_IF_EXISTS;
 using ::cbrtf _LIBCPP_USING_IF_EXISTS;
 
-#if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysign)
+#  if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysign)
 _LIBCPP_HIDE_FROM_ABI constexpr double copysign(double __x, double __y) _NOEXCEPT {
   return __builtin_copysign(__x, __y);
 }
 _LIBCPP_HIDE_FROM_ABI constexpr float copysignf(float __x, float __y) _NOEXCEPT {
   return __builtin_copysignf(__x, __y);
 }
-#else
+#  else
 using ::copysign _LIBCPP_USING_IF_EXISTS;
 using ::copysignf _LIBCPP_USING_IF_EXISTS;
-#endif
+#  endif
 
 using ::erf _LIBCPP_USING_IF_EXISTS;
 using ::erff _LIBCPP_USING_IF_EXISTS;
@@ -527,13 +527,13 @@ using ::asinhl _LIBCPP_USING_IF_EXISTS;
 using ::atanhl _LIBCPP_USING_IF_EXISTS;
 using ::cbrtl _LIBCPP_USING_IF_EXISTS;
 
-#if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysignl)
+#  if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysignl)
 _LIBCPP_HIDE_FROM_ABI constexpr long double copysignl(long double __x, long double __y) _NOEXCEPT {
   return __builtin_copysignl(__x, __y);
 }
-#else
+#  else
 using ::copysignl _LIBCPP_USING_IF_EXISTS;
-#endif
+#  endif
 
 using ::erfl _LIBCPP_USING_IF_EXISTS;
 using ::erfcl _LIBCPP_USING_IF_EXISTS;

>From 77f55c816bbbbcb37986a805925a6fab7c68a636 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 17 Aug 2026 20:09:35 +0200
Subject: [PATCH 5/8] working example

---
 libcxx/include/__math/copysign.h                          | 6 +++++-
 libcxx/include/cmath                                      | 8 +++-----
 libcxx/include/math.h                                     | 1 +
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp   | 4 ++--
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__math/copysign.h b/libcxx/include/__math/copysign.h
index c100c03650733..d55e32b88e16f 100644
--- a/libcxx/include/__math/copysign.h
+++ b/libcxx/include/__math/copysign.h
@@ -29,7 +29,11 @@ copysign(float __x, float __y) _NOEXCEPT {
   return ::__builtin_copysignf(__x, __y);
 }
 
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI double
+// This is a template to avoid conflicting with the C library's non-template `copysign(double, double)` overload
+// when both are visible in the same scope (e.g. after `using std::__math::copysign;` at global scope in <math.h>,
+// where the C library's own declaration for `double` is already present).
+template <class = int>
+[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI double
 copysign(double __x, double __y) _NOEXCEPT {
   return __builtin_copysign(__x, __y);
 }
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index dd53594ea7380..283077b25d174 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -424,15 +424,13 @@ using ::atanhf _LIBCPP_USING_IF_EXISTS;
 using ::cbrt _LIBCPP_USING_IF_EXISTS;
 using ::cbrtf _LIBCPP_USING_IF_EXISTS;
 
-#  if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysign)
-_LIBCPP_HIDE_FROM_ABI constexpr double copysign(double __x, double __y) _NOEXCEPT {
-  return __builtin_copysign(__x, __y);
-}
+using std::__math::copysign;
+
+#  if _LIBCPP_STD_VER >= 23 && __has_constexpr_builtin(__builtin_copysignf)
 _LIBCPP_HIDE_FROM_ABI constexpr float copysignf(float __x, float __y) _NOEXCEPT {
   return __builtin_copysignf(__x, __y);
 }
 #  else
-using ::copysign _LIBCPP_USING_IF_EXISTS;
 using ::copysignf _LIBCPP_USING_IF_EXISTS;
 #  endif
 
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 6f520634d0646..9d0642398b7d4 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -491,6 +491,7 @@ using std::__math::atan2;
 using std::__math::atanh;
 using std::__math::cbrt;
 using std::__math::ceil;
+using std::__math::copysign;
 using std::__math::cos;
 using std::__math::cosh;
 using std::__math::erf;
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..9df526e2bfdd3 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
@@ -155,9 +155,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::remquof(0.5f, 1.0f, &DummyInt) == 0.5f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::remquol(0.5L, 1.0L, &DummyInt) == 0.5L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysign(1.0f, 1.0f) == 1.0f);
+  ASSERT_CONSTEXPR_CXX23(std::copysign(1.0f, 1.0f) == 1.0f);
   ASSERT_CONSTEXPR_CXX23(std::copysign(1.0, 1.0) == 1.0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::copysign(1.0L, 1.0L) == 1.0L);
+  ASSERT_CONSTEXPR_CXX23(std::copysign(1.0L, 1.0L) == 1.0L);
   ASSERT_CONSTEXPR_CXX23(std::copysignf(1.0f, 1.0f) == 1.0f);
   ASSERT_CONSTEXPR_CXX23(std::copysignl(1.0L, 1.0L) == 1.0L);
 

>From 3548e6ab7b7f3b2ef11ffbf936404a4953b99c3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 17 Aug 2026 22:11:31 +0200
Subject: [PATCH 6/8] wip: copysign test

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

diff --git a/libcxx/test/std/numerics/c.math/copysign.pass.cpp b/libcxx/test/std/numerics/c.math/copysign.pass.cpp
new file mode 100644
index 0000000000000..8eef3c01e0f24
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/copysign.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 copysign(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 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::copysign(nan) == FP_NAN);
+    //assert(std::copysign(s_nan) == FP_NAN);
+
+    assert(std::copysign(inf, -inf) == -FP_INFINITE);
+    assert(std::copysign(-inf, inf) == FP_INFINITE);
+
+    assert(std::copysign(T(1), T(-1)) == FP_NORMAL);
+    assert(std::copysign(T(-1), T(1)) == FP_NORMAL);
+    assert(std::copysign(max, -max) == -max);
+    assert(std::copysign(-max, max) == max);
+
+    assert(std::copysign(d_min) == FP_SUBNORMAL);
+
+    assert(std::copysign(T(0)) == FP_ZERO);
+    assert(std::copysign(T(-0.0)) == FP_ZERO);
+  }
+};
+
+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::copysign(T(0)) == FP_ZERO);
+    assert(std::copysign(T(1)) == FP_NORMAL);
+    assert(std::copysign(max) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::copysign(low) == FP_NORMAL);
+      assert(std::copysign(T(-1)) == FP_NORMAL);
+    }
+  }
+};
+
+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 8130542c777dd1771b16419b5f60244a9697235c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 17 Aug 2026 23:00:41 +0200
Subject: [PATCH 7/8] add: working tests

---
 .../std/numerics/c.math/copysign.pass.cpp     | 73 ++++++++++++-------
 1 file changed, 46 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/copysign.pass.cpp b/libcxx/test/std/numerics/c.math/copysign.pass.cpp
index 8eef3c01e0f24..24c6850e43a74 100644
--- a/libcxx/test/std/numerics/c.math/copysign.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/copysign.pass.cpp
@@ -19,54 +19,73 @@
 struct TestFloat {
   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 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();
+    using lim = std::numeric_limits<T>;
 
-    //assert(std::copysign(nan) == FP_NAN);
-    //assert(std::copysign(s_nan) == FP_NAN);
+    assert(std::isnan(std::copysign(lim::quiet_NaN(), T(-1))));
+    assert(std::signbit(std::copysign(lim::signaling_NaN(), T(-1))));
 
-    assert(std::copysign(inf, -inf) == -FP_INFINITE);
-    assert(std::copysign(-inf, inf) == FP_INFINITE);
+    assert(std::copysign(lim::infinity(), -lim::infinity()) == -lim::infinity());
+    assert(std::copysign(-lim::infinity(), lim::infinity()) == lim::infinity());
 
-    assert(std::copysign(T(1), T(-1)) == FP_NORMAL);
-    assert(std::copysign(T(-1), T(1)) == FP_NORMAL);
-    assert(std::copysign(max, -max) == -max);
-    assert(std::copysign(-max, max) == max);
+    assert(std::copysign(T(1), T(-1)) == T(-1));
+    assert(std::copysign(T(-1), T(1)) == T(1));
 
-    assert(std::copysign(d_min) == FP_SUBNORMAL);
+    assert(std::copysign(lim::max(), -lim::max()) == -lim::max());
+    assert(std::copysign(-lim::max(), lim::max()) == lim::max());
 
-    assert(std::copysign(T(0)) == FP_ZERO);
-    assert(std::copysign(T(-0.0)) == FP_ZERO);
+    assert(std::copysign(lim::denorm_min(), T(-1)) == -lim::denorm_min());
+    assert(std::copysign(-lim::denorm_min(), T(1)) == lim::denorm_min());
+
+    assert(std::copysign(T(0), T(-0.0)) == T(0));
+    assert(std::copysign(T(-0.0), T(0)) == T(-0.0));
   }
 };
 
 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();
+    using lim = std::numeric_limits<T>;
+
+    // no negative-zero bit pattern.
+    assert(std::copysign(lim::max(), T(-0)) == static_cast<double>(lim::max()));
+    assert(std::copysign(T(-0), lim::max()) == T(0));
 
-    assert(std::copysign(T(0)) == FP_ZERO);
-    assert(std::copysign(T(1)) == FP_NORMAL);
-    assert(std::copysign(max) == FP_NORMAL);
+    if (lim::is_signed) {
+      assert(std::copysign(T(1), T(-1)) == T(-1));
+      assert(std::copysign(T(-1), T(1)) == T(1));
 
-    if (std::is_signed<T>::value) {
-      assert(std::copysign(low) == FP_NORMAL);
-      assert(std::copysign(T(-1)) == FP_NORMAL);
+      assert(std::copysign(T(1), lim::lowest()) == T(-1));
+      assert(std::copysign(lim::lowest(), T(-1)) == lim::lowest());
     }
   }
 };
 
 TEST_CONSTEXPR_CXX23 bool test() {
+  using lim = std::numeric_limits<double>;
+
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::copysign` with mixed-type promotions.
+  {
+    assert(std::copysign(2.0, -1) == -2.0);                  // double vs int
+    assert(std::copysign(-1, 2.0f) == 1);                    // int vs float
+    assert(std::copysign(2.0L, -1.0f) == -2.0L);             // long double vs float
+    assert(std::isnan(std::copysign(lim::quiet_NaN(), -1))); // NaN vs int
+  }
+
+  // std::copysignf(float, float).
+  {
+    assert(std::copysignf(2.0f, -1.0f) == -2.0f);
+    assert(std::copysignf(-2.0f, 1.0f) == 2.0f);
+  }
+
+  // std::copysignl(long double, long double).
+  {
+    assert(std::copysignl(2.0L, -1.0L) == -2.0L);
+    assert(std::copysignl(-2.0L, 1.0L) == 2.0L);
+  }
+
   return true;
 }
 

>From 484ca15d8c70d9932c598cd55cf63a43d9d242f1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 18 Aug 2026 01:20:27 +0200
Subject: [PATCH 8/8] fix: deactivate test for windows

---
 libcxx/test/std/numerics/c.math/copysign.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/copysign.pass.cpp b/libcxx/test/std/numerics/c.math/copysign.pass.cpp
index 24c6850e43a74..580813d5ba7c5 100644
--- a/libcxx/test/std/numerics/c.math/copysign.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/copysign.pass.cpp
@@ -8,6 +8,9 @@
 
 // bool copysign(floating-point-type x, floating-point-type y); // constexpr since C++23
 
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
 #include <cassert>
 #include <cmath>
 #include <limits>



More information about the libcxx-commits mailing list