[libc++][math] Add constexpr to std::fpclassify (#210993)

Adds `constexpr` to `std::fpclassify` as required by
[P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf).

Towards https://github.com/llvm/llvm-project/issues/105174. Follows-up
8889a3d3b5.
This commit is contained in:
Lucas Mellone 2026-07-26 08:30:25 +02:00 committed by GitHub
parent 3fd07e3235
commit 4eeff3e309
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 118 additions and 15 deletions

View file

@ -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);

View file

@ -387,26 +387,50 @@ namespace __math {
// fpclassify
// 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 {
# 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);
}
template <class = int>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
[[__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);
}
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 _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_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
[[__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 {
return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
}
template <class = int>
[[__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_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_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
return __x == 0 ? FP_ZERO : FP_NORMAL;
}
# endif
} // namespace __math

View file

@ -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);

View file

@ -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);

View file

@ -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>
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(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(max) == FP_NORMAL);
assert(std::fpclassify(min) == FP_NORMAL);
assert(std::fpclassify(d_min) == FP_SUBNORMAL);
assert(std::fpclassify(T(0)) == FP_ZERO);
assert(std::fpclassify(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::fpclassify(T(0)) == FP_ZERO);
assert(std::fpclassify(T(1)) == FP_NORMAL);
assert(std::fpclassify(max) == FP_NORMAL);
if (std::is_signed<T>::value) {
assert(std::fpclassify(low) == FP_NORMAL);
assert(std::fpclassify(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;
}