Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<cmath>: Complete additional overloads for integers #4537

Merged
merged 12 commits into from
Apr 9, 2024
91 changes: 90 additions & 1 deletion stl/inc/cmath
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,50 @@ _STD _Common_float_type_t<_Ty1, _Ty2> remquo(_Ty1 _Left, _Ty2 _Right, int* _Pquo
}
}

// TRANSITION, GH-519, should be provided by UCRT
template <class _Ty, _STD enable_if_t<_STD is_integral_v<_Ty>, int> = 0>
_NODISCARD _Check_return_ _CONSTEXPR23 int fpclassify(_In_ const _Ty _Ix) noexcept /* strengthened */ {
return _Ix == 0 ? FP_ZERO : FP_NORMAL;
}

// TRANSITION, GH-519, should be provided by UCRT
template <class _Ty, _STD enable_if_t<_STD is_integral_v<_Ty>, int> = 0>
_NODISCARD _Check_return_ _CONSTEXPR23 bool signbit(_In_ const _Ty _Ix) noexcept /* strengthened */ {
if constexpr (static_cast<_Ty>(-1) < _Ty{}) {
return _Ix < 0;
} else {
return false;
}
}

// TRANSITION, GH-519, additional overloads are not templated to avoid ambiguity with the major overload in UCRT
#define _GENERIC_MATH_ISNORMAL(TYPE) \
_NODISCARD _Check_return_ _CONSTEXPR23 bool isnormal(_In_ const TYPE _Ix) noexcept /* strengthened */ { \
return _Ix != 0; \
}

_GENERIC_MATH_ISNORMAL(signed char)
_GENERIC_MATH_ISNORMAL(unsigned char)
_GENERIC_MATH_ISNORMAL(short)
_GENERIC_MATH_ISNORMAL(unsigned short)
_GENERIC_MATH_ISNORMAL(int)
_GENERIC_MATH_ISNORMAL(unsigned int)
_GENERIC_MATH_ISNORMAL(long)
_GENERIC_MATH_ISNORMAL(unsigned long)
_GENERIC_MATH_ISNORMAL(long long)
_GENERIC_MATH_ISNORMAL(unsigned long long)
_GENERIC_MATH_ISNORMAL(bool)
_GENERIC_MATH_ISNORMAL(char)
#ifdef __cpp_char8_t
_GENERIC_MATH_ISNORMAL(char8_t)
#endif // defined(__cpp_char8_t)
_GENERIC_MATH_ISNORMAL(char16_t)
_GENERIC_MATH_ISNORMAL(char32_t)
#ifdef _NATIVE_WCHAR_T_DEFINED
_GENERIC_MATH_ISNORMAL(wchar_t)
#endif // defined(_NATIVE_WCHAR_T_DEFINED)
#undef _GENERIC_MATH_ISNORMAL

#define _GENERIC_MATH1_BASE(NAME, RET, FUN) \
template <class _Ty, _STD enable_if_t<_STD is_integral_v<_Ty>, int> = 0> \
_NODISCARD RET NAME(_Ty _Left) noexcept /* strengthened */ { \
Expand Down Expand Up @@ -640,6 +684,42 @@ _STD _Common_float_type_t<_Ty1, _Ty2> remquo(_Ty1 _Left, _Ty2 _Right, int* _Pquo
#define _GENERIC_MATH2I(FUN, CLANG_INTRIN, MSVC_INTRIN) _GENERIC_MATH2_BASE(FUN, _CSTD FUN)
#endif // ^^^ intrinsics unavailable ^^^

// TRANSITION, GH-519, additional overloads are not templated to avoid ambiguity with the major overload in UCRT
#define _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, TYPE) \
_NODISCARD _Check_return_ _CONSTEXPR23 bool FUN(_In_ TYPE) noexcept /* strengthened */ { \
return RETV; \
}

#ifdef __cpp_char8_t
#define _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV) _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char8_t)
#else // ^^^ defined(__cpp_char8_t) / !defined(__cpp_char8_t) vvv
#define _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV)
#endif // ^^^ !defined(__cpp_char8_t) ^^^

#ifdef _NATIVE_WCHAR_T_DEFINED
#define _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV) _GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, wchar_t)
#else // ^^^ defined(_NATIVE_WCHAR_T_DEFINED) / !defined(_NATIVE_WCHAR_T_DEFINED) vvv
#define _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV)
#endif // ^^^ !defined(_NATIVE_WCHAR_T_DEFINED) ^^^

#define _GENERIC_MATH_CLASSIFY1_RETV(FUN, RETV) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, signed char) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned char) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, short) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned short) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, int) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned int) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, long) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned long) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, long long) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, unsigned long long) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, bool) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char) \
_GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T(FUN, RETV) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char16_t) \
_GENERIC_MATH_CLASSIFY1_RETV_INTEGER(FUN, RETV, char32_t) \
_GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T(FUN, RETV)

// The following order matches N4950 [cmath.syn].
_GENERIC_MATH1(acos)
_GENERIC_MATH1(asin)
Expand Down Expand Up @@ -701,7 +781,12 @@ _GENERIC_MATH2(fmax)
_GENERIC_MATH2(fmin)
// fma() is hand-crafted
// lerp() is hand-crafted
// The "classification/comparison functions" (fpclassify(), etc.) are exempt, LWG-1327
// fpclassify() is hand-crafted
_GENERIC_MATH_CLASSIFY1_RETV(isfinite, true)
_GENERIC_MATH_CLASSIFY1_RETV(isinf, false)
_GENERIC_MATH_CLASSIFY1_RETV(isnan, false)
// isnormal() is half-hand-crafted
// signbit() is hand-crafted

#undef _GENERIC_MATH1_BASE
#undef _GENERIC_MATH1R
Expand All @@ -711,6 +796,10 @@ _GENERIC_MATH2(fmin)
#undef _GENERIC_MATH2_BASE
#undef _GENERIC_MATH2
#undef _GENERIC_MATH2I
#undef _GENERIC_MATH_CLASSIFY1_RETV_INTEGER
#undef _GENERIC_MATH_CLASSIFY1_RETV_WCHAR_T
#undef _GENERIC_MATH_CLASSIFY1_RETV_CHAR8_T
#undef _GENERIC_MATH_CLASSIFY1_RETV
#undef _HAS_CMATH_INTRINSICS
_END_EXTERN_CXX_WORKAROUND

Expand Down
4 changes: 0 additions & 4 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -484,10 +484,6 @@ std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp:2


# *** STL BUGS ***
# GH-519 <cmath>: signbit() misses overloads for integer types
std/depr/depr.c.headers/math_h.pass.cpp FAIL
std/numerics/c.math/cmath.pass.cpp FAIL

# GH-784 <type_traits>: aligned_storage has incorrect alignment defaults
std/utilities/meta/meta.trans/meta.trans.other/aligned_storage.pass.cpp FAIL

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ tests\GH_000431_lex_compare_family
tests\GH_000431_lex_compare_memcmp_classify
tests\GH_000442_random_subtract_with_carry_engine_io
tests\GH_000457_system_error_message
tests\GH_000519_cmath_int_overloads
tests\GH_000527_remove_allocator_void
tests\GH_000545_include_compare
tests\GH_000625_vector_bool_optimization
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/GH_000519_cmath_int_overloads/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
190 changes: 190 additions & 0 deletions tests/std/tests/GH_000519_cmath_int_overloads/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <cmath>
#include <math.h>
#include <type_traits>

// Also test the partial implementation of P0533R9 "constexpr For <cmath> And <cstdlib>"
#if _HAS_CXX23
#define CONSTEXPR23 constexpr
#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv
#define CONSTEXPR23 inline
#endif // ^^^ !_HAS_CXX23 ^^^

CONSTEXPR23 void test_bool_overloads() {
// test overloads in std

assert(std::fpclassify(false) == FP_ZERO);
assert(std::fpclassify(true) == FP_NORMAL);

assert(std::isfinite(false));
assert(std::isfinite(true));

assert(!std::isinf(false));
assert(!std::isinf(true));

assert(!std::isnan(false));
assert(!std::isnan(true));

assert(!std::isnormal(false));
assert(std::isnormal(true));

assert(!std::signbit(false));
assert(!std::signbit(true));

#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For <cmath> And <cstdlib>"
if (!std::is_constant_evaluated())
#endif // ^^^ _HAS_CXX23 ^^^
{
assert(!std::isgreater(false, true));
assert(!std::isgreaterequal(false, true));
assert(std::isless(false, true));
assert(std::islessequal(false, true));
assert(std::islessgreater(false, true));
assert(!std::isunordered(false, true));
}

// test overloads in the global namespace

assert(::fpclassify(false) == FP_ZERO);
assert(::fpclassify(true) == FP_NORMAL);

assert(::isfinite(false));
assert(::isfinite(true));

assert(!::isinf(false));
assert(!::isinf(true));

assert(!::isnan(false));
assert(!::isnan(true));

assert(!::isnormal(false));
assert(::isnormal(true));

assert(!::signbit(false));
assert(!::signbit(true));

#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For <cmath> And <cstdlib>"
if (!std::is_constant_evaluated())
#endif // ^^^ _HAS_CXX23 ^^^
{
assert(!::isgreater(false, true));
assert(!::isgreaterequal(false, true));
assert(::isless(false, true));
assert(::islessequal(false, true));
assert(::islessgreater(false, true));
assert(!::isunordered(false, true));
}
}

template <class I>
CONSTEXPR23 void test_other_integral_overloads() {
// test overloads in std

assert(std::fpclassify(I{}) == FP_ZERO);
assert(std::fpclassify(static_cast<I>(42)) == FP_NORMAL);
assert(std::fpclassify(static_cast<I>(-42)) == FP_NORMAL);

assert(std::isfinite(I{}));
assert(std::isfinite(static_cast<I>(42)));
assert(std::isfinite(static_cast<I>(-42)));

assert(!std::isinf(I{}));
assert(!std::isinf(static_cast<I>(42)));
assert(!std::isinf(static_cast<I>(-42)));

assert(!std::isnan(I{}));
assert(!std::isnan(static_cast<I>(42)));
assert(!std::isnan(static_cast<I>(-42)));

assert(!std::isnormal(I{}));
assert(std::isnormal(static_cast<I>(42)));
assert(std::isnormal(static_cast<I>(-42)));

assert(!std::signbit(I{}));
assert(!std::signbit(static_cast<I>(42)));
assert(std::signbit(static_cast<I>(-42)) == std::is_signed_v<I>);

#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For <cmath> And <cstdlib>"
if (!std::is_constant_evaluated())
#endif // ^^^ _HAS_CXX23 ^^^
{
assert(!std::isgreater(static_cast<I>(17), static_cast<I>(29)));
assert(!std::isgreaterequal(static_cast<I>(17), static_cast<I>(29)));
assert(std::isless(static_cast<I>(17), static_cast<I>(29)));
assert(std::islessequal(static_cast<I>(17), static_cast<I>(29)));
assert(std::islessgreater(static_cast<I>(17), static_cast<I>(29)));
assert(!std::isunordered(static_cast<I>(17), static_cast<I>(29)));
}

// test overloads in the global namespace

assert(::fpclassify(I{}) == FP_ZERO);
assert(::fpclassify(static_cast<I>(42)) == FP_NORMAL);
assert(::fpclassify(static_cast<I>(-42)) == FP_NORMAL);

assert(::isfinite(I{}));
assert(::isfinite(static_cast<I>(42)));
assert(::isfinite(static_cast<I>(-42)));

assert(!::isinf(I{}));
assert(!::isinf(static_cast<I>(42)));
assert(!::isinf(static_cast<I>(-42)));

assert(!::isnan(I{}));
assert(!::isnan(static_cast<I>(42)));
assert(!::isnan(static_cast<I>(-42)));

assert(!::isnormal(I{}));
assert(::isnormal(static_cast<I>(42)));
assert(::isnormal(static_cast<I>(-42)));

assert(!::signbit(I{}));
assert(!::signbit(static_cast<I>(42)));
assert(::signbit(static_cast<I>(-42)) == std::is_signed_v<I>);

#if _HAS_CXX23 // TRANSITION, UCRT should implement P0533R9 "constexpr For <cmath> And <cstdlib>"
if (!std::is_constant_evaluated())
#endif // ^^^ _HAS_CXX23 ^^^
{
assert(!::isgreater(static_cast<I>(17), static_cast<I>(29)));
assert(!::isgreaterequal(static_cast<I>(17), static_cast<I>(29)));
assert(::isless(static_cast<I>(17), static_cast<I>(29)));
assert(::islessequal(static_cast<I>(17), static_cast<I>(29)));
assert(::islessgreater(static_cast<I>(17), static_cast<I>(29)));
assert(!::isunordered(static_cast<I>(17), static_cast<I>(29)));
}
}

CONSTEXPR23 bool test_all_integral_overloads() {
test_bool_overloads();
test_other_integral_overloads<signed char>();
test_other_integral_overloads<unsigned char>();
test_other_integral_overloads<short>();
test_other_integral_overloads<unsigned short>();
test_other_integral_overloads<int>();
test_other_integral_overloads<unsigned int>();
test_other_integral_overloads<long>();
test_other_integral_overloads<unsigned long>();
test_other_integral_overloads<long long>();
test_other_integral_overloads<unsigned long long>();
test_other_integral_overloads<char>();
#ifdef __cpp_char8_t
test_other_integral_overloads<char8_t>();
#endif // defined(__cpp_char8_t)
test_other_integral_overloads<char16_t>();
test_other_integral_overloads<char32_t>();
test_other_integral_overloads<wchar_t>();

return true;
}

#if _HAS_CXX23
static_assert(test_all_integral_overloads());
#endif // _HAS_CXX23

int main() {
test_all_integral_overloads();
}
Loading