Skip to content

Commit

Permalink
<cmath>: Complete additional overloads for integers (#4537)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
frederick-vs-ja and StephanTLavavej authored Apr 9, 2024
1 parent be81252 commit d655083
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 5 deletions.
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();
}

0 comments on commit d655083

Please sign in to comment.