Skip to content

Commit

Permalink
[libc++][math] Add constexpr for std::signbit() (#105946)
Browse files Browse the repository at this point in the history
## 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)
  • Loading branch information
robincaloudis authored Sep 5, 2024
1 parent eae1d61 commit 1a12647
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 6 deletions.
13 changes: 10 additions & 3 deletions libcxx/include/__math/traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,25 @@ namespace __math {

// signbit

// TODO(LLVM 22): Remove conditional once support for Clang 19 is dropped.
#if defined(_LIBCPP_COMPILER_GCC) || __has_constexpr_builtin(__builtin_signbit)
# define _LIBCPP_SIGNBIT_CONSTEXPR _LIBCPP_CONSTEXPR_SINCE_CXX23
#else
# define _LIBCPP_SIGNBIT_CONSTEXPR
#endif

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_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1 __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_SIGNBIT_CONSTEXPR _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_SIGNBIT_CONSTEXPR _LIBCPP_HIDE_FROM_ABI bool signbit(_A1) _NOEXCEPT {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,16 @@ int main(int, char**) {
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0) == 1);
ASSERT_CONSTEXPR_CXX23(std::isnormal(-1.0L) == 1);

// TODO(LLVM 22): Remove `__has_constexpr_builtin` conditional once support for Clang 19 is dropped.
#if !__has_constexpr_builtin(__builtin_signbit)
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);
#else
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0f) == 1);
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
#endif

ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
78 changes: 78 additions & 0 deletions libcxx/test/std/numerics/c.math/signbit.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// 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

// These compilers don't support constexpr `__builtin_signbit` yet.
// UNSUPPORTED: clang-17, clang-18, clang-19, apple-clang-15, apple-clang-16

#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(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>::quiet_NaN()));
assert(!std::signbit(std::numeric_limits<T>::signaling_NaN()));
assert(std::signbit(-T(0)));
assert(std::signbit(-std::numeric_limits<T>::infinity()));
assert(std::signbit(std::numeric_limits<T>::lowest()));

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::signbit(std::numeric_limits<T>::max()));
assert(!std::signbit(T(0)));
if (std::is_unsigned<T>::value) {
assert(!std::signbit(std::numeric_limits<T>::lowest()));
} else {
assert(std::signbit(std::numeric_limits<T>::lowest()));
}

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;
}

0 comments on commit 1a12647

Please sign in to comment.