Skip to content

Commit 1a12647

Browse files
[libc++][math] Add constexpr for std::signbit() (#105946)
## Why Since 18th of August, the floating point comparison builtin ``__builtin_signbit`` is available in Clang as constant expression (#94118). ## What * Implement `constexpr` for `std::signbit()` as defined by [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf) (new C++23 feature) * Restrict execution of tests to tip-of-trunk Clang as builtin is not yet available (note that builtin is available in GCC)
1 parent eae1d61 commit 1a12647

File tree

4 files changed

+98
-6
lines changed

4 files changed

+98
-6
lines changed

libcxx/include/__math/traits.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,25 @@ namespace __math {
2727

2828
// signbit
2929

30+
// TODO(LLVM 22): Remove conditional once support for Clang 19 is dropped.
31+
#if defined(_LIBCPP_COMPILER_GCC) || __has_constexpr_builtin(__builtin_signbit)
32+
# define _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_CONSTEXPR_SINCE_CXX23
33+
#else
34+
# define _LIBCPP_SIGNBIT_CONSTEXPR
35+
#endif
36+
3037
template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
31-
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
38+
_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
3239
return __builtin_signbit(__x);
3340
}
3441

3542
template <class _A1, __enable_if_t<is_integral<_A1>::value && is_signed<_A1>::value, int> = 0>
36-
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
43+
_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __x) _NOEXCEPT {
3744
return __x < 0;
3845
}
3946

4047
template <class _A1, __enable_if_t<is_integral<_A1>::value && !is_signed<_A1>::value, int> = 0>
41-
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
48+
_LIBCPP_NODISCARD inline _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
4249
return false;
4350
}
4451

libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,16 @@ int main(int, char**) {
220220
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
221221
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
222222

223+
// TODO(LLVM 22): Remove `__has_constexpr_builtin` conditional once support for Clang 19 is dropped.
224+
#if !__has_constexpr_builtin(__builtin_signbit)
223225
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
224226
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
225227
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
228+
#else
229+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
230+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
231+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
232+
#endif
226233

227234
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
228235
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);

libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,9 @@ int main(int, char**) {
217217
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
218218
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);
219219

220-
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
221-
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
222-
ASSERT_NOT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
220+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
221+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
222+
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
223223

224224
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
225225
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// bool signbit(floating-point-type x); // constexpr since C++23
10+
11+
// We don't control the implementation on windows
12+
// UNSUPPORTED: windows
13+
14+
// These compilers don't support constexpr `__builtin_signbit` yet.
15+
// UNSUPPORTED: clang-17, clang-18, clang-19, apple-clang-15, apple-clang-16
16+
17+
#include <cassert>
18+
#include <cmath>
19+
#include <limits>
20+
21+
#include "test_macros.h"
22+
#include "type_algorithms.h"
23+
24+
struct TestFloat {
25+
template <class T>
26+
static TEST_CONSTEXPR_CXX23 bool test() {
27+
assert(!std::signbit(T(0)));
28+
assert(!std::signbit(std::numeric_limits<T>::min()));
29+
assert(!std::signbit(std::numeric_limits<T>::denorm_min()));
30+
assert(!std::signbit(std::numeric_limits<T>::max()));
31+
assert(!std::signbit(std::numeric_limits<T>::infinity()));
32+
assert(!std::signbit(std::numeric_limits<T>::quiet_NaN()));
33+
assert(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
34+
assert(std::signbit(-T(0)));
35+
assert(std::signbit(-std::numeric_limits<T>::infinity()));
36+
assert(std::signbit(std::numeric_limits<T>::lowest()));
37+
38+
return true;
39+
}
40+
41+
template <class T>
42+
TEST_CONSTEXPR_CXX23 void operator()() {
43+
test<T>();
44+
#if TEST_STD_VER >= 23
45+
static_assert(test<T>());
46+
#endif
47+
}
48+
};
49+
50+
struct TestInt {
51+
template <class T>
52+
static TEST_CONSTEXPR_CXX23 bool test() {
53+
assert(!std::signbit(std::numeric_limits<T>::max()));
54+
assert(!std::signbit(T(0)));
55+
if (std::is_unsigned<T>::value) {
56+
assert(!std::signbit(std::numeric_limits<T>::lowest()));
57+
} else {
58+
assert(std::signbit(std::numeric_limits<T>::lowest()));
59+
}
60+
61+
return true;
62+
}
63+
64+
template <class T>
65+
TEST_CONSTEXPR_CXX23 void operator()() {
66+
test<T>();
67+
#if TEST_STD_VER >= 23
68+
static_assert(test<T>());
69+
#endif
70+
}
71+
};
72+
73+
int main(int, char**) {
74+
types::for_each(types::floating_point_types(), TestFloat());
75+
types::for_each(types::integral_types(), TestInt());
76+
77+
return 0;
78+
}

0 commit comments

Comments
 (0)