[libcxx-commits] [libcxx] [libc++][math] Add `constexpr` for `std::signbit()` (PR #105946)
Robin Caloudis via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 27 02:36:00 PDT 2024
https://github.com/robincaloudis updated https://github.com/llvm/llvm-project/pull/105946
>From dbcf63d399fe86a4823ece39f6373083790c5b07 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:00:25 +0200
Subject: [PATCH 1/7] Test constexpr for signbit()
---
.../c.math/constexpr-cxx23-clang.pass.cpp | 6 +-
.../c.math/constexpr-cxx23-gcc.pass.cpp | 6 +-
.../test/std/numerics/c.math/signbit.pass.cpp | 128 ++++++++++++++++++
libcxx/test/support/type_algorithms.h | 20 ++-
4 files changed, 147 insertions(+), 13 deletions(-)
create mode 100644 libcxx/test/std/numerics/c.math/signbit.pass.cpp
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 a07260a34516f1..20887b8cf2678b 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
@@ -220,9 +220,9 @@ int main(int, char**) {
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 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 8c481f41a945ed..d8779706bcee22 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
@@ -217,9 +217,9 @@ int main(int, char**) {
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
- ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
+ ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
new file mode 100644
index 00000000000000..9d8a3305a11325
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 signbit(floating-point-type x); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+ template <class T>
+ static TEST_CONSTEXPR_CXX23 bool test() {
+ assert(!std::signbit(T(0)));
+ assert(std::signbit(-T(0)));
+ assert(std::signbit(std::numeric_limits<T>::lowest()));
+ assert(!std::signbit(std::numeric_limits<T>::min()));
+ assert(!std::signbit(std::numeric_limits<T>::denorm_min()));
+ assert(!std::signbit(std::numeric_limits<T>::max()));
+ assert(!std::signbit(std::numeric_limits<T>::infinity()));
+ assert(std::signbit(-std::numeric_limits<T>::infinity()));
+ assert(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
+ assert(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
+
+ return true;
+ }
+
+ template <class T>
+ TEST_CONSTEXPR_CXX23 void operator()() {
+ test<T>();
+#if TEST_STD_VER >= 23
+ static_assert(test<T>());
+#endif
+ }
+};
+
+struct TestUnsignedIntAndFixedWidthChar {
+ template <class T>
+ static TEST_CONSTEXPR_CXX23 bool test() {
+ assert(!std::signbit(std::numeric_limits<T>::max()));
+ assert(!std::signbit(std::numeric_limits<T>::lowest()));
+ assert(!std::signbit(T(0)));
+
+ return true;
+ }
+
+ template <class T>
+ TEST_CONSTEXPR_CXX23 void operator()() {
+ test<T>();
+#if TEST_STD_VER >= 23
+ static_assert(test<T>());
+#endif
+ }
+};
+
+struct TestSignedIntAndVariableWidthChar {
+ template <class T>
+ static TEST_CONSTEXPR_CXX23 bool test() {
+ assert(!std::signbit(std::numeric_limits<T>::max()));
+ assert(std::signbit(std::numeric_limits<T>::lowest()));
+ assert(!std::signbit(T(0)));
+
+ return true;
+ }
+
+// `char`defaults to be a `unsigned char` and `wchar_t` detaults to be a `unsigned int`
+// on PowerPC as well as ARM (contrary to e.g. x86). Therefore `std::lowest()` returns 0.
+#if defined(__arm__) || defined(__powerpc__)
+ template <>
+ TEST_CONSTEXPR_CXX23 bool test<char>() {
+ assert(!std::signbit(std::numeric_limits<char>::max()));
+ assert(!std::signbit(std::numeric_limits<char>::lowest()));
+ assert(!std::signbit(char(0)));
+
+ return true;
+ }
+ template <>
+ TEST_CONSTEXPR_CXX23 bool test<wchar_t>() {
+ assert(!std::signbit(std::numeric_limits<wchar_t>::max()));
+ assert(!std::signbit(std::numeric_limits<wchar_t>::lowest()));
+ assert(!std::signbit(wchar_t(0)));
+
+ return true;
+ }
+#endif
+
+ template <class T>
+ TEST_CONSTEXPR_CXX23 void operator()() {
+ test<T>();
+#if TEST_STD_VER >= 23
+ static_assert(test<T>());
+#endif
+ }
+};
+
+template <typename T>
+struct ConvertibleTo {
+ operator T() const { return T(); }
+};
+
+int main(int, char**) {
+ types::for_each(types::floating_point_types(), TestFloat());
+ types::for_each(types::concatenate_t<types::unsigned_integer_types, types::fixed_width_character_types>(),
+ TestUnsignedIntAndFixedWidthChar());
+ types::for_each(types::concatenate_t<types::signed_integer_types, types::variable_width_character_types>(),
+ TestSignedIntAndVariableWidthChar());
+
+ // Make sure we can call `std::signbit` with convertible types. This checks
+ // whether overloads for all cv-unqualified floating-point types are working
+ // as expected.
+ {
+ assert(!std::signbit(ConvertibleTo<float>()));
+ assert(!std::signbit(ConvertibleTo<double>()));
+ assert(!std::signbit(ConvertibleTo<long double>()));
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
index da3d0add4d0c45..6b3b9aba11e5ec 100644
--- a/libcxx/test/support/type_algorithms.h
+++ b/libcxx/test/support/type_algorithms.h
@@ -82,22 +82,28 @@ struct partial_instantiation {
// type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers)
-using character_types =
+using variable_width_character_types =
type_list<char
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
,
wchar_t
#endif
+ >;
+
+using fixed_width_character_types = type_list<
#ifndef TEST_HAS_NO_CHAR8_T
- ,
- char8_t
+ char8_t
#endif
#if TEST_STD_VER >= 11
- ,
- char16_t,
- char32_t
+# ifndef TEST_HAS_NO_CHAR8_T
+ ,
+# endif
+ char16_t,
+ char32_t
#endif
- >;
+ >;
+
+using character_types = concatenate_t<variable_width_character_types, fixed_width_character_types>;
using signed_integer_types =
type_list<signed char,
>From f4dfa154eb895ed419a41acd3fd0dbbbcf551aa8 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:00:56 +0200
Subject: [PATCH 2/7] Add constexpr for signbit()
---
libcxx/include/__math/traits.h | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 35c283cc9e21ce..deff6139493032 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -29,17 +29,29 @@ namespace __math {
// signbit
template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+ return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+ return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+ return __builtin_signbit(__x);
+}
+
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
return __builtin_signbit(__x);
}
template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
return __x < 0;
}
template <class _A1, __enable_if_t<is_integral<_A1>::value && !is_signed<_A1>::value, int> = 0>
-_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
return false;
}
>From 666530fb76017623204e0e8086da720866b8ebef Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 18:01:43 +0200
Subject: [PATCH 3/7] Update cxx23 status
---
libcxx/docs/Status/Cxx23.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 1a8d43bff74752..e977da82884269 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -38,7 +38,7 @@ Paper Status
.. note::
- .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
+ .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan``, ``isnormal`` and ``signbit`` are implemented.
.. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
clang doesn't issue a diagnostic for deprecated using template declarations.
.. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
>From 0041bb8c47e420f7bcac511f22bbc7e2d8e8ca06 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sat, 24 Aug 2024 20:51:54 +0200
Subject: [PATCH 4/7] Use signcopy workaround
If constexpr signbit is not available, we make
use of signcopy(), which has been constexpr for
quite a while in Clang.
---
libcxx/include/__math/traits.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index deff6139493032..9670b056160141 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -30,19 +30,39 @@ namespace __math {
template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+ return __builtin_copysign(1.0, __x) == -1.0;
+#else
return __builtin_signbit(__x);
+#endif
}
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+ return __builtin_copysign(1.0, __x) == -1.0;
+#else
return __builtin_signbit(__x);
+#endif
}
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+ return __builtin_copysign(1.0, __x) == -1.0;
+#else
return __builtin_signbit(__x);
+#endif
}
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
+// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
+#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
+ return __builtin_copysign(1.0, __x) == -1.0;
+#else
return __builtin_signbit(__x);
+#endif
}
template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
>From 5a6e37cdfcc44337e897838c89414bfca7f5b0b5 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sun, 25 Aug 2024 00:39:30 +0200
Subject: [PATCH 5/7] Prefer libcxx overload
By using `_LIBCPP_PREFERRED_OVERLOAD` we make
sure that a given overload is a better match
than an otherwise equally good function
declaration.
Why is there an equally good function
declaration in the first place? Underlying the
Windows SDK is the UCRT, the universal C runtime,
which clang-cl makes use of. The UCRT should
provide only C library headers, but does on top
comes with overloads for all cv-unqualified floating
point types (float, double, long double) for
`std::signbit()` in https://github.com/microsoft/win32metadata/blob/e012b29924c53aa941fc010850b68331b0c3ea80/generation/WinSDK/RecompiledIdlHeaders/ucrt/corecrt_math.h#L309-L322.
In a certain way, this can be seen as a deviation
from the C standard. We need to work around it.
---
libcxx/include/__math/traits.h | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 9670b056160141..598ec4e35f88ea 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -38,7 +38,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
#endif
}
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(float __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+ bool
+ signbit(float __x) _NOEXCEPT {
// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
return __builtin_copysign(1.0, __x) == -1.0;
@@ -47,7 +52,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
#endif
}
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(double __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+ bool
+ signbit(double __x) _NOEXCEPT {
// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
return __builtin_copysign(1.0, __x) == -1.0;
@@ -56,7 +66,12 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
#endif
}
-_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool signbit(long double __x) _NOEXCEPT {
+_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI
+#ifdef _LIBCPP_PREFERRED_OVERLOAD
+_LIBCPP_PREFERRED_OVERLOAD
+#endif
+ bool
+ signbit(long double __x) _NOEXCEPT {
// TODO(LLVM 22): Remove `__builtin_copysign`-workaround once support for Clang 19 is dropped.
#if !__has_constexpr_builtin(__builtin_signbit) && _LIBCPP_STD_VER >= 23
return __builtin_copysign(1.0, __x) == -1.0;
>From 696d98f6698f0c7e5efd4ea760a7477613c12843 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Sun, 25 Aug 2024 13:45:49 +0200
Subject: [PATCH 6/7] Assert constexpr at compile time
---
.../test/std/numerics/c.math/signbit.pass.cpp | 93 ++++++++-----------
1 file changed, 40 insertions(+), 53 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
index 9d8a3305a11325..a87e1d5bfd188c 100644
--- a/libcxx/test/std/numerics/c.math/signbit.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -18,94 +18,81 @@
#include "test_macros.h"
#include "type_algorithms.h"
+#if TEST_STD_VER >= 23
+# define COMPILE_OR_RUNTIME_ASSERT(expr) static_assert(expr);
+#else
+# define COMPILE_OR_RUNTIME_ASSERT(expr) assert(expr);
+#endif
+
struct TestFloat {
template <class T>
- static TEST_CONSTEXPR_CXX23 bool test() {
- assert(!std::signbit(T(0)));
- assert(std::signbit(-T(0)));
- assert(std::signbit(std::numeric_limits<T>::lowest()));
- assert(!std::signbit(std::numeric_limits<T>::min()));
- assert(!std::signbit(std::numeric_limits<T>::denorm_min()));
- assert(!std::signbit(std::numeric_limits<T>::max()));
- assert(!std::signbit(std::numeric_limits<T>::infinity()));
- assert(std::signbit(-std::numeric_limits<T>::infinity()));
- assert(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
- assert(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
-
- return true;
+ static void test() {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(-T(0)));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::min()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::denorm_min()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::infinity()));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(-std::numeric_limits<T>::infinity()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
}
template <class T>
- TEST_CONSTEXPR_CXX23 void operator()() {
+ void operator()() {
test<T>();
-#if TEST_STD_VER >= 23
- static_assert(test<T>());
-#endif
}
};
struct TestUnsignedIntAndFixedWidthChar {
template <class T>
- static TEST_CONSTEXPR_CXX23 bool test() {
- assert(!std::signbit(std::numeric_limits<T>::max()));
- assert(!std::signbit(std::numeric_limits<T>::lowest()));
- assert(!std::signbit(T(0)));
-
- return true;
+ static void test() {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::lowest()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
}
template <class T>
- TEST_CONSTEXPR_CXX23 void operator()() {
+ void operator()() {
test<T>();
-#if TEST_STD_VER >= 23
- static_assert(test<T>());
-#endif
}
};
struct TestSignedIntAndVariableWidthChar {
template <class T>
- static TEST_CONSTEXPR_CXX23 bool test() {
- assert(!std::signbit(std::numeric_limits<T>::max()));
- assert(std::signbit(std::numeric_limits<T>::lowest()));
- assert(!std::signbit(T(0)));
-
- return true;
+ static void test() {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
}
// `char`defaults to be a `unsigned char` and `wchar_t` detaults to be a `unsigned int`
// on PowerPC as well as ARM (contrary to e.g. x86). Therefore `std::lowest()` returns 0.
#if defined(__arm__) || defined(__powerpc__)
template <>
- TEST_CONSTEXPR_CXX23 bool test<char>() {
- assert(!std::signbit(std::numeric_limits<char>::max()));
- assert(!std::signbit(std::numeric_limits<char>::lowest()));
- assert(!std::signbit(char(0)));
-
- return true;
+ void test<char>() {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<char>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<char>::lowest()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(char(0)));
}
template <>
- TEST_CONSTEXPR_CXX23 bool test<wchar_t>() {
- assert(!std::signbit(std::numeric_limits<wchar_t>::max()));
- assert(!std::signbit(std::numeric_limits<wchar_t>::lowest()));
- assert(!std::signbit(wchar_t(0)));
-
- return true;
+ void test<wchar_t>() {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<wchar_t>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<wchar_t>::lowest()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(wchar_t(0)));
}
#endif
template <class T>
- TEST_CONSTEXPR_CXX23 void operator()() {
+ void operator()() {
test<T>();
-#if TEST_STD_VER >= 23
- static_assert(test<T>());
-#endif
}
};
template <typename T>
struct ConvertibleTo {
- operator T() const { return T(); }
+ TEST_CONSTEXPR_CXX23 operator T() const { return T(); }
};
int main(int, char**) {
@@ -119,9 +106,9 @@ int main(int, char**) {
// whether overloads for all cv-unqualified floating-point types are working
// as expected.
{
- assert(!std::signbit(ConvertibleTo<float>()));
- assert(!std::signbit(ConvertibleTo<double>()));
- assert(!std::signbit(ConvertibleTo<long double>()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(ConvertibleTo<float>()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(ConvertibleTo<double>()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(ConvertibleTo<long double>()));
}
return 0;
>From 957c11a1b3806702b603bb02a5a88b38e2690bb7 Mon Sep 17 00:00:00 2001
From: Robin Caloudis <robin.caloudis at gmx.de>
Date: Tue, 27 Aug 2024 10:58:39 +0200
Subject: [PATCH 7/7] Use type trait instead of preprocessor macro
---
.../test/std/numerics/c.math/signbit.pass.cpp | 63 ++++++++-----------
1 file changed, 27 insertions(+), 36 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/signbit.pass.cpp b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
index a87e1d5bfd188c..86a596f22c9277 100644
--- a/libcxx/test/std/numerics/c.math/signbit.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/signbit.pass.cpp
@@ -14,6 +14,7 @@
#include <cassert>
#include <cmath>
#include <limits>
+#include <type_traits>
#include "test_macros.h"
#include "type_algorithms.h"
@@ -26,67 +27,57 @@
struct TestFloat {
template <class T>
- static void test() {
+ void operator()() {
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
- COMPILE_OR_RUNTIME_ASSERT(std::signbit(-T(0)));
- COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::min()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::denorm_min()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::infinity()));
- COMPILE_OR_RUNTIME_ASSERT(std::signbit(-std::numeric_limits<T>::infinity()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
- }
-
- template <class T>
- void operator()() {
- test<T>();
+ // Signed
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(-T(0)));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(-std::numeric_limits<T>::infinity()));
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
}
};
struct TestUnsignedIntAndFixedWidthChar {
template <class T>
- static void test() {
+ void operator()() {
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::lowest()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
+ // Unsigned
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::lowest()));
}
+};
+struct TestSignedInt {
template <class T>
void operator()() {
- test<T>();
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
+ // Signed
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
}
};
-struct TestSignedIntAndVariableWidthChar {
+struct TestVariableWidthChar {
template <class T>
- static void test() {
+ void operator()() {
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<T>::max()));
- COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<T>::lowest()));
COMPILE_OR_RUNTIME_ASSERT(!std::signbit(T(0)));
+ // Signed or unsigned depending on the architecture and platform.
+ test_signbit_with_changing_sign_in_lowest<T>();
}
-// `char`defaults to be a `unsigned char` and `wchar_t` detaults to be a `unsigned int`
-// on PowerPC as well as ARM (contrary to e.g. x86). Therefore `std::lowest()` returns 0.
-#if defined(__arm__) || defined(__powerpc__)
- template <>
- void test<char>() {
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<char>::max()));
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<char>::lowest()));
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(char(0)));
- }
- template <>
- void test<wchar_t>() {
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<wchar_t>::max()));
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<wchar_t>::lowest()));
- COMPILE_OR_RUNTIME_ASSERT(!std::signbit(wchar_t(0)));
- }
-#endif
-
template <class T>
- void operator()() {
- test<T>();
+ void test_signbit_with_changing_sign_in_lowest() {
+ if constexpr (std::is_unsigned_v<T>) {
+ COMPILE_OR_RUNTIME_ASSERT(!std::signbit(std::numeric_limits<wchar_t>::lowest()));
+ } else {
+ COMPILE_OR_RUNTIME_ASSERT(std::signbit(std::numeric_limits<wchar_t>::lowest()));
+ }
}
};
@@ -99,8 +90,8 @@ int main(int, char**) {
types::for_each(types::floating_point_types(), TestFloat());
types::for_each(types::concatenate_t<types::unsigned_integer_types, types::fixed_width_character_types>(),
TestUnsignedIntAndFixedWidthChar());
- types::for_each(types::concatenate_t<types::signed_integer_types, types::variable_width_character_types>(),
- TestSignedIntAndVariableWidthChar());
+ types::for_each(types::signed_integer_types(), TestSignedInt());
+ types::for_each(types::variable_width_character_types(), TestVariableWidthChar());
// Make sure we can call `std::signbit` with convertible types. This checks
// whether overloads for all cv-unqualified floating-point types are working
More information about the libcxx-commits
mailing list