зеркало из https://github.com/microsoft/STL.git
Finish P0811R3 midpoint and lerp (#1048)
* Removes workaround for missing `bit_cast` and mark `lerp` constexpr. * Changes how `lerp` handles infinite inputs according to https://github.com/microsoft/STL/issues/65#issuecomment-563811523 and https://github.com/microsoft/STL/issues/65#issuecomment-564102550. * Adds constexpr tests. Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
This commit is contained in:
Родитель
0e7b5d2509
Коммит
e9f56a6148
116
stl/inc/cmath
116
stl/inc/cmath
|
@ -12,6 +12,10 @@
|
|||
#include <cstdlib>
|
||||
#include <xtr1common>
|
||||
|
||||
#if _HAS_CXX20
|
||||
#include <xutility>
|
||||
#endif // _HAS_CXX20
|
||||
|
||||
#pragma pack(push, _CRT_PACKING)
|
||||
#pragma warning(push, _STL_WARNING_LEVEL)
|
||||
#pragma warning(disable : _STL_DISABLED_WARNINGS)
|
||||
|
@ -1238,13 +1242,12 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) {
|
|||
|
||||
#if _HAS_CXX20
|
||||
// FUNCTION lerp
|
||||
// TRANSITION, P0553: lerp is not yet constexpr
|
||||
template <class _Ty>
|
||||
_NODISCARD /* constexpr */ _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) noexcept {
|
||||
_NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) noexcept {
|
||||
// on a line intersecting {(0.0, _ArgA), (1.0, _ArgB)}, return the Y value for X == _ArgT
|
||||
|
||||
const int _Finite_mask = (int{isfinite(_ArgA)} << 2) | (int{isfinite(_ArgB)} << 1) | int{isfinite(_ArgT)};
|
||||
if (_Finite_mask == 0b111) {
|
||||
const bool _T_is_finite = _STD _Is_finite(_ArgT);
|
||||
if (_T_is_finite && _STD _Is_finite(_ArgA) && _STD _Is_finite(_ArgB)) {
|
||||
// 99% case, put it first; this block comes from P0811R3
|
||||
if ((_ArgA <= 0 && _ArgB >= 0) || (_ArgA >= 0 && _ArgB <= 0)) {
|
||||
// exact, monotonic, bounded, determinate, and (for _ArgA == _ArgB == 0) consistent:
|
||||
|
@ -1272,96 +1275,61 @@ _NODISCARD /* constexpr */ _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, co
|
|||
return _Candidate;
|
||||
}
|
||||
|
||||
if (isnan(_ArgA)) {
|
||||
return _ArgA;
|
||||
}
|
||||
|
||||
if (isnan(_ArgB)) {
|
||||
return _ArgB;
|
||||
}
|
||||
|
||||
if (isnan(_ArgT)) {
|
||||
return _ArgT;
|
||||
}
|
||||
|
||||
switch (_Finite_mask) {
|
||||
case 0b000:
|
||||
// All values are infinities
|
||||
if (_ArgT >= 1) {
|
||||
return _ArgB;
|
||||
}
|
||||
|
||||
return _ArgA;
|
||||
case 0b010:
|
||||
case 0b100:
|
||||
case 0b110:
|
||||
// _ArgT is an infinity; return infinity in the "direction" of _ArgA and _ArgB
|
||||
return _ArgT * (_ArgB - _ArgA);
|
||||
case 0b001:
|
||||
// Here _ArgA and _ArgB are infinities
|
||||
if (_ArgA == _ArgB) {
|
||||
// same sign, so T doesn't matter
|
||||
if (_STD is_constant_evaluated()) {
|
||||
if (_STD _Is_nan(_ArgA)) {
|
||||
return _ArgA;
|
||||
}
|
||||
|
||||
// Opposite signs, choose the "infinity direction" according to T if it makes sense.
|
||||
if (_ArgT <= 0) {
|
||||
return _ArgA;
|
||||
}
|
||||
|
||||
if (_ArgT >= 1) {
|
||||
if (_STD _Is_nan(_ArgB)) {
|
||||
return _ArgB;
|
||||
}
|
||||
|
||||
// Interpolating between infinities of opposite signs doesn't make sense, NaN
|
||||
if constexpr (sizeof(_Ty) == sizeof(float)) {
|
||||
return __builtin_nanf("0");
|
||||
if (_STD _Is_nan(_ArgT)) {
|
||||
return _ArgT;
|
||||
}
|
||||
} else {
|
||||
// raise FE_INVALID if at least one of _ArgA, _ArgB, and _ArgT is signaling NaN
|
||||
if (_STD _Is_nan(_ArgA) || _STD _Is_nan(_ArgB)) {
|
||||
return (_ArgA + _ArgB) + _ArgT;
|
||||
}
|
||||
|
||||
if (_STD _Is_nan(_ArgT)) {
|
||||
return _ArgT + _ArgT;
|
||||
}
|
||||
}
|
||||
|
||||
if (_T_is_finite) {
|
||||
// _ArgT is finite, _ArgA and/or _ArgB is infinity
|
||||
if (_ArgT < 0) {
|
||||
// if _ArgT < 0: return infinity in the "direction" of _ArgA if that exists, NaN otherwise
|
||||
return _ArgA - _ArgB;
|
||||
} else if (_ArgT <= 1) {
|
||||
// if _ArgT == 0: return _ArgA (infinity) if _ArgB is finite, NaN otherwise
|
||||
// if 0 < _ArgT < 1: return infinity "between" _ArgA and _ArgB if that exists, NaN otherwise
|
||||
// if _ArgT == 1: return _ArgB (infinity) if _ArgA is finite, NaN otherwise
|
||||
return _ArgT * _ArgB + (1 - _ArgT) * _ArgA;
|
||||
} else {
|
||||
return __builtin_nan("0");
|
||||
// if _ArgT > 1: return infinity in the "direction" of _ArgB if that exists, NaN otherwise
|
||||
return _ArgB - _ArgA;
|
||||
}
|
||||
case 0b011:
|
||||
// _ArgA is an infinity but _ArgB is not
|
||||
if (_ArgT == 1) {
|
||||
return _ArgB;
|
||||
}
|
||||
|
||||
if (_ArgT < 1) {
|
||||
// towards the infinity, return it
|
||||
return _ArgA;
|
||||
}
|
||||
|
||||
// away from the infinity
|
||||
return -_ArgA;
|
||||
case 0b101:
|
||||
// _ArgA is finite and _ArgB is an infinity
|
||||
if (_ArgT == 0) {
|
||||
return _ArgA;
|
||||
}
|
||||
|
||||
if (_ArgT > 0) {
|
||||
// toward the infinity
|
||||
return _ArgB;
|
||||
}
|
||||
|
||||
return -_ArgB;
|
||||
case 0b111: // impossible; handled in fast path
|
||||
default:
|
||||
_CSTD abort();
|
||||
} else {
|
||||
// _ArgT is an infinity; return infinity in the "direction" of _ArgA and _ArgB if that exists, NaN otherwise
|
||||
return _ArgT * (_ArgB - _ArgA);
|
||||
}
|
||||
}
|
||||
|
||||
// As of 2019-06-17 it is unclear whether the "sufficient additional overloads" clause is intended to target lerp;
|
||||
// LWG-3223 is pending.
|
||||
|
||||
_NODISCARD /* constexpr */ inline float lerp(const float _ArgA, const float _ArgB, const float _ArgT) noexcept {
|
||||
_NODISCARD constexpr inline float lerp(const float _ArgA, const float _ArgB, const float _ArgT) noexcept {
|
||||
return _Common_lerp(_ArgA, _ArgB, _ArgT);
|
||||
}
|
||||
|
||||
_NODISCARD /* constexpr */ inline double lerp(const double _ArgA, const double _ArgB, const double _ArgT) noexcept {
|
||||
_NODISCARD constexpr inline double lerp(const double _ArgA, const double _ArgB, const double _ArgT) noexcept {
|
||||
return _Common_lerp(_ArgA, _ArgB, _ArgT);
|
||||
}
|
||||
|
||||
_NODISCARD /* constexpr */ inline long double lerp(
|
||||
_NODISCARD constexpr inline long double lerp(
|
||||
const long double _ArgA, const long double _ArgB, const long double _ArgT) noexcept {
|
||||
return _Common_lerp(_ArgA, _ArgB, _ArgT);
|
||||
}
|
||||
|
|
|
@ -536,21 +536,16 @@ _CONSTEXPR20 void iota(_FwdIt _First, _FwdIt _Last, _Ty _Val) {
|
|||
|
||||
#if _HAS_CXX17
|
||||
// FUNCTION TEMPLATE _Abs_u
|
||||
template <class _Arithmetic>
|
||||
_NODISCARD constexpr auto _Abs_u(const _Arithmetic _Val) noexcept {
|
||||
template <class _Integral>
|
||||
_NODISCARD constexpr auto _Abs_u(const _Integral _Val) noexcept {
|
||||
// computes absolute value of _Val (converting to an unsigned integer type if necessary to avoid overflow
|
||||
// representing the negation of the minimum value)
|
||||
if constexpr (is_floating_point_v<_Arithmetic>) {
|
||||
// TRANSITION, P0553: this mishandles NaNs
|
||||
if (_Val < 0) {
|
||||
return -_Val;
|
||||
}
|
||||
static_assert(is_integral_v<_Integral>);
|
||||
|
||||
return _Val;
|
||||
} else if constexpr (is_signed_v<_Arithmetic>) {
|
||||
using _Unsigned = make_unsigned_t<_Arithmetic>;
|
||||
if constexpr (is_signed_v<_Integral>) {
|
||||
using _Unsigned = make_unsigned_t<_Integral>;
|
||||
if (_Val < 0) {
|
||||
// note static_cast to _Unsigned such that _Arithmetic == short returns unsigned short rather than int
|
||||
// note static_cast to _Unsigned such that _Integral == short returns unsigned short rather than int
|
||||
return static_cast<_Unsigned>(_Unsigned{0} - static_cast<_Unsigned>(_Val));
|
||||
}
|
||||
|
||||
|
@ -619,9 +614,24 @@ _NODISCARD constexpr common_type_t<_Mt, _Nt> lcm(const _Mt _Mx, const _Nt _Nx) n
|
|||
template <class _Ty, enable_if_t<is_arithmetic_v<_Ty> && !is_same_v<remove_cv_t<_Ty>, bool>, int> = 0>
|
||||
_NODISCARD constexpr _Ty midpoint(const _Ty _Val1, const _Ty _Val2) noexcept {
|
||||
if constexpr (is_floating_point_v<_Ty>) {
|
||||
if (_STD is_constant_evaluated()) {
|
||||
if (_STD _Is_nan(_Val1)) {
|
||||
return _Val1;
|
||||
}
|
||||
|
||||
if (_STD _Is_nan(_Val2)) {
|
||||
return _Val2;
|
||||
}
|
||||
} else {
|
||||
if (_STD _Is_nan(_Val1) || _STD _Is_nan(_Val2)) {
|
||||
// raise FE_INVALID if at least one of _Val1 and _Val2 is signaling NaN
|
||||
return _Val1 + _Val2;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr _Ty _High_limit = (numeric_limits<_Ty>::max)() / 2;
|
||||
const auto _Val1_a = _Abs_u(_Val1);
|
||||
const auto _Val2_a = _Abs_u(_Val2);
|
||||
const auto _Val1_a = _Float_abs(_Val1);
|
||||
const auto _Val2_a = _Float_abs(_Val2);
|
||||
if (_Val1_a <= _High_limit && _Val2_a <= _High_limit) {
|
||||
// _Val1 and _Val2 are small enough that _Val1 + _Val2 won't overflow
|
||||
|
||||
|
@ -637,22 +647,12 @@ _NODISCARD constexpr _Ty midpoint(const _Ty _Val1, const _Ty _Val2) noexcept {
|
|||
return (_Val1 + _Val2) / 2;
|
||||
}
|
||||
|
||||
// TRANSITION, P0553: the next two branches handle NaNs but don't produce correct behavior under /fp:fast or
|
||||
// -fassociative-math
|
||||
if (_Val1 != _Val1) {
|
||||
return _Val1;
|
||||
}
|
||||
|
||||
if (_Val2 != _Val2) {
|
||||
return _Val2;
|
||||
}
|
||||
|
||||
// Here at least one of {_Val1, _Val2} has large magnitude.
|
||||
// Therefore, if one of the values is too small to divide by 2 exactly, the small magnitude is much less than
|
||||
// one ULP of the result, so we can add it directly without the potentially inexact division by 2.
|
||||
|
||||
// In the default rounding mode this less than one ULP difference will always be rounded away, so under
|
||||
// /fp:precise or /fp:fast we could avoid these tests if we had some means of detecting it in the caller.
|
||||
// /fp:fast we could avoid these tests if we had some means of detecting it in the caller.
|
||||
constexpr _Ty _Low_limit = (numeric_limits<_Ty>::min)() * 2;
|
||||
if (_Val1_a < _Low_limit) {
|
||||
return _Val1 + _Val2 / 2;
|
||||
|
|
|
@ -27,6 +27,12 @@ _STL_DISABLE_CLANG_WARNINGS
|
|||
#define _USE_STD_VECTOR_ALGORITHMS 0
|
||||
#endif
|
||||
|
||||
#ifdef __CUDACC__
|
||||
#define _CONSTEXPR_BIT_CAST inline
|
||||
#else // ^^^ workaround ^^^ / vvv no workaround vvv
|
||||
#define _CONSTEXPR_BIT_CAST constexpr
|
||||
#endif // ^^^ no workaround ^^^
|
||||
|
||||
#if _USE_STD_VECTOR_ALGORITHMS
|
||||
_EXTERN_C
|
||||
// The "noalias" attribute tells the compiler optimizer that pointers going into these hand-vectorized algorithms
|
||||
|
@ -52,17 +58,15 @@ template <class _To, class _From,
|
|||
enable_if_t<conjunction_v<bool_constant<sizeof(_To) == sizeof(_From)>, is_trivially_copyable<_To>,
|
||||
is_trivially_copyable<_From>>,
|
||||
int> = 0>
|
||||
_NODISCARD _CONSTEXPR_BIT_CAST _To _Bit_cast(const _From& _Val) noexcept {
|
||||
#ifdef __CUDACC__
|
||||
_NODISCARD _To _Bit_cast(const _From& _Val) noexcept {
|
||||
_To _To_obj; // assumes default-init
|
||||
_CSTD memcpy(_STD addressof(_To_obj), _STD addressof(_Val), sizeof(_To));
|
||||
return _To_obj;
|
||||
}
|
||||
#else // ^^^ workaround ^^^ / vvv no workaround vvv
|
||||
_NODISCARD constexpr _To _Bit_cast(const _From& _Val) noexcept {
|
||||
return __builtin_bit_cast(_To, _Val);
|
||||
}
|
||||
#endif // ^^^ no workaround ^^^
|
||||
}
|
||||
|
||||
// STRUCT TEMPLATE _Get_first_parameter
|
||||
template <class _Ty>
|
||||
|
@ -1149,19 +1153,19 @@ struct _Iterator_traits_base<_Iter,
|
|||
typename _Iter::pointer, typename _Iter::reference>> {
|
||||
// defined if _Iter::* types exist
|
||||
using iterator_category = typename _Iter::iterator_category;
|
||||
using value_type = typename _Iter::value_type;
|
||||
using difference_type = typename _Iter::difference_type;
|
||||
using pointer = typename _Iter::pointer;
|
||||
using reference = typename _Iter::reference;
|
||||
using value_type = typename _Iter::value_type;
|
||||
using difference_type = typename _Iter::difference_type;
|
||||
using pointer = typename _Iter::pointer;
|
||||
using reference = typename _Iter::reference;
|
||||
};
|
||||
|
||||
template <class _Ty, bool = is_object_v<_Ty>>
|
||||
struct _Iterator_traits_pointer_base { // iterator properties for pointers to object
|
||||
using iterator_category = random_access_iterator_tag;
|
||||
using value_type = remove_cv_t<_Ty>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = _Ty*;
|
||||
using reference = _Ty&;
|
||||
using value_type = remove_cv_t<_Ty>;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = _Ty*;
|
||||
using reference = _Ty&;
|
||||
};
|
||||
|
||||
template <class _Ty>
|
||||
|
@ -1421,8 +1425,8 @@ _NODISCARD constexpr decltype(auto) _Get_unwrapped_n(_Iter&& _It, const _Diff _O
|
|||
template <class _Iter, class _Diff, enable_if_t<_Unwrappable_for_offset_v<_Iter> && is_integral_v<_Diff>, int> = 0>
|
||||
_NODISCARD constexpr decltype(auto) _Get_unwrapped_n(_Iter&& _It, const _Diff _Off) {
|
||||
// ask an iterator to assert that the iterator moved _Off positions is valid, and unwrap
|
||||
using _IDiff = _Iter_diff_t<_Remove_cvref_t<_Iter>>;
|
||||
using _CDiff = common_type_t<_Diff, _IDiff>;
|
||||
using _IDiff = _Iter_diff_t<_Remove_cvref_t<_Iter>>;
|
||||
using _CDiff = common_type_t<_Diff, _IDiff>;
|
||||
const auto _COff = static_cast<_CDiff>(_Off);
|
||||
|
||||
_STL_ASSERT(_COff <= static_cast<_CDiff>(_Max_possible_v<_IDiff>)
|
||||
|
@ -1731,7 +1735,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) {
|
|||
// increment iterator by offset, input iterators
|
||||
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");
|
||||
|
||||
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
|
||||
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
|
||||
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;
|
||||
|
||||
for (; 0 < _Off; --_Off) {
|
||||
|
@ -1746,7 +1750,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) {
|
|||
template <class _BidIt, class _Diff>
|
||||
_CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag) {
|
||||
// increment iterator by offset, bidirectional iterators
|
||||
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
|
||||
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
|
||||
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;
|
||||
|
||||
for (; 0 < _Off; --_Off) {
|
||||
|
@ -1799,8 +1803,8 @@ template <class _InIt>
|
|||
_CONSTEXPR17 _Iter_diff_t<_InIt> _Distance1(_InIt _First, _InIt _Last, input_iterator_tag) {
|
||||
// return distance between iterators; input
|
||||
_Adl_verify_range(_First, _Last);
|
||||
auto _UFirst = _Get_unwrapped(_First);
|
||||
const auto _ULast = _Get_unwrapped(_Last);
|
||||
auto _UFirst = _Get_unwrapped(_First);
|
||||
const auto _ULast = _Get_unwrapped(_Last);
|
||||
_Iter_diff_t<_InIt> _Off = 0;
|
||||
for (; _UFirst != _ULast; ++_UFirst) {
|
||||
++_Off;
|
||||
|
@ -4431,7 +4435,7 @@ _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { // copy [_First, _
|
|||
const _Algorithm_int_t<_Diff> _Count = _Count_raw;
|
||||
if (0 < _Count) {
|
||||
auto _UFirst = _Get_unwrapped_n(_First, _Count);
|
||||
auto _UDest = _Get_unwrapped_n(_Dest, _Count);
|
||||
auto _UDest = _Get_unwrapped_n(_Dest, _Count);
|
||||
_Seek_wrapped(
|
||||
_Dest, _Copy_n_unchecked4(_UFirst, _Count, _UDest,
|
||||
bool_constant<_Ptr_copy_cat<decltype(_UFirst), decltype(_UDest)>::_Trivially_copyable>{}));
|
||||
|
@ -4518,9 +4522,9 @@ _BidIt2 _Copy_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest, t
|
|||
template <class _BidIt1, class _BidIt2>
|
||||
_BidIt2 copy_backward(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { // copy [_First, _Last) backwards to [..., _Dest)
|
||||
_Adl_verify_range(_First, _Last);
|
||||
auto _UFirst = _Get_unwrapped(_First);
|
||||
auto _UFirst = _Get_unwrapped(_First);
|
||||
const auto _ULast = _Get_unwrapped(_Last);
|
||||
auto _UDest = _Get_unwrapped_n(_Dest, -_Idl_distance<_BidIt1>(_UFirst, _ULast));
|
||||
auto _UDest = _Get_unwrapped_n(_Dest, -_Idl_distance<_BidIt1>(_UFirst, _ULast));
|
||||
_Seek_wrapped(_Dest, _Copy_backward_unchecked(_UFirst, _ULast, _UDest,
|
||||
bool_constant<_Ptr_copy_cat<decltype(_UFirst), decltype(_UDest)>::_Trivially_copyable>{}));
|
||||
return _Dest;
|
||||
|
@ -4969,7 +4973,7 @@ bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _F
|
|||
// compare [_First1, _Last1) to [_First2, ...), memcmp optimization
|
||||
const auto _First1_ch = reinterpret_cast<const char*>(_First1);
|
||||
const auto _First2_ch = reinterpret_cast<const char*>(_First2);
|
||||
const auto _Count = static_cast<size_t>(reinterpret_cast<const char*>(_Last1) - _First1_ch);
|
||||
const auto _Count = static_cast<size_t>(reinterpret_cast<const char*>(_Last1) - _First1_ch);
|
||||
return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0;
|
||||
}
|
||||
|
||||
|
@ -4978,7 +4982,7 @@ _NODISCARD bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _F
|
|||
// compare [_First1, _Last1) to [_First2, ...)
|
||||
_Adl_verify_range(_First1, _Last1);
|
||||
const auto _UFirst1 = _Get_unwrapped(_First1);
|
||||
const auto _ULast1 = _Get_unwrapped(_Last1);
|
||||
const auto _ULast1 = _Get_unwrapped(_Last1);
|
||||
const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_InIt1>(_UFirst1, _ULast1));
|
||||
return _Equal_unchecked(_UFirst1, _ULast1, _UFirst2, _Pass_fn(_Pred));
|
||||
}
|
||||
|
@ -5867,7 +5871,53 @@ struct _CXX17_DEPRECATE_ITERATOR_BASE_CLASS iterator { // base type for iterator
|
|||
using pointer = _Pointer;
|
||||
using reference = _Reference;
|
||||
};
|
||||
|
||||
// STRUCT TEMPLATE _Float_traits
|
||||
template <class _Ty>
|
||||
struct _Float_traits {
|
||||
static_assert(is_floating_point_v<_Ty>, "_Float_traits<NonFloatingPoint> is invalid");
|
||||
|
||||
// traits for double and long double:
|
||||
using type = unsigned long long;
|
||||
|
||||
static constexpr type _Magnitude_mask = 0x7fff'ffff'ffff'ffffULL;
|
||||
static constexpr type _Exponent_mask = 0x7ff0'0000'0000'0000ULL;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct _Float_traits<float> {
|
||||
using type = unsigned int;
|
||||
|
||||
static constexpr type _Magnitude_mask = 0x7fff'ffffU;
|
||||
static constexpr type _Exponent_mask = 0x7f80'0000U;
|
||||
};
|
||||
|
||||
// FUNCTION TEMPLATE _Float_abs_bits
|
||||
template <class _Ty, enable_if_t<is_floating_point_v<_Ty>, int> = 0>
|
||||
_NODISCARD _CONSTEXPR_BIT_CAST auto _Float_abs_bits(const _Ty& _Xx) {
|
||||
const auto _Bits = _Bit_cast<typename _Float_traits<_Ty>::type>(_Xx);
|
||||
return _Bits & _Float_traits<_Ty>::_Magnitude_mask;
|
||||
}
|
||||
|
||||
// FUNCTION TEMPLATE _Float_abs
|
||||
template <class _Ty, enable_if_t<is_floating_point_v<_Ty>, int> = 0>
|
||||
_NODISCARD _CONSTEXPR_BIT_CAST _Ty _Float_abs(const _Ty _Xx) { // constexpr floating point abs()
|
||||
return _Bit_cast<_Ty>(_Float_abs_bits(_Xx));
|
||||
}
|
||||
|
||||
// FUNCTION TEMPLATE _Is_nan
|
||||
template <class _Ty, enable_if_t<is_floating_point_v<_Ty>, int> = 0>
|
||||
_NODISCARD _CONSTEXPR_BIT_CAST bool _Is_nan(const _Ty _Xx) { // constexpr isnan()
|
||||
return _Float_abs_bits(_Xx) > _Float_traits<_Ty>::_Exponent_mask;
|
||||
}
|
||||
|
||||
// FUNCTION TEMPLATE _Is_finite
|
||||
template <class _Ty, enable_if_t<is_floating_point_v<_Ty>, int> = 0>
|
||||
_NODISCARD _CONSTEXPR_BIT_CAST bool _Is_finite(const _Ty _Xx) { // constexpr isfinite()
|
||||
return _Float_abs_bits(_Xx) < _Float_traits<_Ty>::_Exponent_mask;
|
||||
}
|
||||
_STD_END
|
||||
#undef _CONSTEXPR_BIT_CAST
|
||||
#pragma pop_macro("new")
|
||||
_STL_RESTORE_CLANG_WARNINGS
|
||||
#pragma warning(pop)
|
||||
|
|
|
@ -164,7 +164,6 @@
|
|||
// (partially implemented)
|
||||
// P0769R2 shift_left(), shift_right()
|
||||
// P0811R3 midpoint(), lerp()
|
||||
// (partially implemented, lerp() not yet constexpr)
|
||||
// P0879R0 constexpr For Swapping Functions
|
||||
// P0887R1 type_identity
|
||||
// P0896R4 Ranges
|
||||
|
@ -1169,6 +1168,7 @@
|
|||
#define __cpp_lib_generic_unordered_lookup 201811L
|
||||
#define __cpp_lib_int_pow2 202002L
|
||||
#define __cpp_lib_integer_comparison_functions 202002L
|
||||
#define __cpp_lib_interpolate 201902L
|
||||
#define __cpp_lib_is_constant_evaluated 201811L
|
||||
#define __cpp_lib_is_nothrow_convertible 201806L
|
||||
#define __cpp_lib_list_remove_return_type 201806L
|
||||
|
|
|
@ -465,10 +465,6 @@ std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp FAIL
|
|||
# C++20 P0768R1 "Library Support for the Spaceship (Comparison) Operator"
|
||||
std/language.support/support.limits/support.limits.general/compare.version.pass.cpp FAIL
|
||||
|
||||
# C++20 P0811R2 "midpoint(), lerp()"
|
||||
std/language.support/support.limits/support.limits.general/numeric.version.pass.cpp FAIL
|
||||
std/numerics/c.math/c.math.lerp/c.math.lerp.pass.cpp FAIL
|
||||
|
||||
# C++20 P0896R4 "<ranges>"
|
||||
std/language.support/support.limits/support.limits.general/algorithm.version.pass.cpp FAIL
|
||||
std/language.support/support.limits/support.limits.general/functional.version.pass.cpp FAIL
|
||||
|
@ -775,6 +771,11 @@ std/iterators/predef.iterators/insert.iterators/insert.iterator/types.pass.cpp F
|
|||
std/numerics/complex.number/cmplx.over/conj.pass.cpp:0 FAIL
|
||||
std/numerics/complex.number/cmplx.over/proj.pass.cpp:0 FAIL
|
||||
|
||||
# Assertion failed: (std::lerp(T(2.3), T(2.3), inf) == T(2.3))
|
||||
# Asserts `(std::lerp(T(2.3), T(2.3), inf) == T(2.3))` and `std::isnan(std::lerp(T( 0), T( 0), inf))`
|
||||
# They shouldn't behave differently. Both of them should probably return NaN.
|
||||
std/numerics/c.math/c.math.lerp/c.math.lerp.pass.cpp FAIL
|
||||
|
||||
|
||||
# *** LIKELY STL BUGS ***
|
||||
# Not yet analyzed, likely STL bugs. Assertions and other runtime failures.
|
||||
|
|
|
@ -465,10 +465,6 @@ utilities\variant\variant.variant\variant.ctor\T.pass.cpp
|
|||
# C++20 P0768R1 "Library Support for the Spaceship (Comparison) Operator"
|
||||
language.support\support.limits\support.limits.general\compare.version.pass.cpp
|
||||
|
||||
# C++20 P0811R2 "midpoint(), lerp()"
|
||||
language.support\support.limits\support.limits.general\numeric.version.pass.cpp
|
||||
numerics\c.math\c.math.lerp\c.math.lerp.pass.cpp
|
||||
|
||||
# C++20 P0896R4 "<ranges>"
|
||||
language.support\support.limits\support.limits.general\algorithm.version.pass.cpp
|
||||
language.support\support.limits\support.limits.general\functional.version.pass.cpp
|
||||
|
@ -775,6 +771,11 @@ iterators\predef.iterators\insert.iterators\insert.iterator\types.pass.cpp
|
|||
numerics\complex.number\cmplx.over\conj.pass.cpp
|
||||
numerics\complex.number\cmplx.over\proj.pass.cpp
|
||||
|
||||
# Assertion failed: (std::lerp(T(2.3), T(2.3), inf) == T(2.3))
|
||||
# Asserts `(std::lerp(T(2.3), T(2.3), inf) == T(2.3))` and `std::isnan(std::lerp(T( 0), T( 0), inf))`
|
||||
# They shouldn't behave differently. Both of them should probably return NaN.
|
||||
numerics\c.math\c.math.lerp\c.math.lerp.pass.cpp
|
||||
|
||||
|
||||
# *** LIKELY STL BUGS ***
|
||||
# Not yet analyzed, likely STL bugs. Assertions and other runtime failures.
|
||||
|
|
|
@ -2,3 +2,6 @@
|
|||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
RUNALL_INCLUDE ..\usual_latest_matrix.lst
|
||||
RUNALL_CROSSLIST
|
||||
PM_CL="/Od"
|
||||
PM_CL="/O2"
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <bit>
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <fenv.h>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -19,13 +23,16 @@ using namespace std;
|
|||
template <typename Ty>
|
||||
using limits = numeric_limits<Ty>;
|
||||
|
||||
// "major" floating point exceptions, excluding underflow and inexact
|
||||
constexpr int fe_major_except = FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW;
|
||||
|
||||
#ifdef _M_FP_STRICT
|
||||
// According to:
|
||||
// https://docs.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior
|
||||
// Under the default /fp:precise mode:
|
||||
// The compiler generates code intended to run in the default floating-point environment and assumes that the
|
||||
// floating-point environment is not accessed or modified at runtime.
|
||||
// ... so we only do testing of rounding modes when strict is enabled.
|
||||
// ... so we only do testing of rounding modes and floating-point exceptions when strict is enabled.
|
||||
|
||||
// TRANSITION, VSO-923474 -- should be #pragma STDC FENV_ACCESS ON
|
||||
#pragma fenv_access(on)
|
||||
|
@ -51,49 +58,89 @@ public:
|
|||
private:
|
||||
int oldRound;
|
||||
};
|
||||
#endif // _M_FP_STRICT
|
||||
|
||||
void checked_feholdexcept(fenv_t* const env) {
|
||||
[[maybe_unused]] const int holdExcept = feholdexcept(env);
|
||||
assert(holdExcept == 0);
|
||||
}
|
||||
|
||||
void checked_fesetenv(const fenv_t* const env) {
|
||||
[[maybe_unused]] const int setEnv = fesetenv(env);
|
||||
assert(setEnv == 0);
|
||||
}
|
||||
|
||||
class ExceptGuard {
|
||||
public:
|
||||
ExceptGuard() {
|
||||
checked_feholdexcept(&env);
|
||||
}
|
||||
|
||||
ExceptGuard(const ExceptGuard&) = delete;
|
||||
ExceptGuard& operator=(const ExceptGuard&) = delete;
|
||||
|
||||
~ExceptGuard() {
|
||||
checked_fesetenv(&env);
|
||||
}
|
||||
|
||||
private:
|
||||
fenv_t env;
|
||||
};
|
||||
|
||||
bool check_feexcept(const int expected_excepts, const int except_mask = fe_major_except) {
|
||||
return fetestexcept(except_mask) == (expected_excepts & except_mask);
|
||||
}
|
||||
#else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv
|
||||
class ExceptGuard {
|
||||
public:
|
||||
ExceptGuard() {}
|
||||
|
||||
ExceptGuard(const ExceptGuard&) = delete;
|
||||
ExceptGuard& operator=(const ExceptGuard&) = delete;
|
||||
|
||||
~ExceptGuard() {}
|
||||
};
|
||||
|
||||
bool check_feexcept(
|
||||
[[maybe_unused]] const int expected_excepts, [[maybe_unused]] const int except_mask = fe_major_except) {
|
||||
return true;
|
||||
}
|
||||
#endif // ^^^ !defined(_M_FP_STRICT) ^^^
|
||||
|
||||
template <class Ty>
|
||||
Ty mint_nan(const bool sign, const unsigned long long payload);
|
||||
constexpr Ty mint_nan(const bool sign, const unsigned long long payload);
|
||||
|
||||
template <>
|
||||
float mint_nan<float>(const bool sign, const unsigned long long payload) {
|
||||
const unsigned int filteredPayload = payload & 0x7F'FFFFu; // bottom 23 bits
|
||||
constexpr float mint_nan<float>(const bool sign, const unsigned long long payload) {
|
||||
const unsigned int filteredPayload = payload & 0x3F'FFFFu; // bottom 22 bits
|
||||
assert(filteredPayload == payload); // if this assert fails, payload didn't fit
|
||||
assert(filteredPayload != 0); // if this assert fails, the NaN would be an infinity
|
||||
|
||||
// clang-format off
|
||||
const unsigned int result =
|
||||
(static_cast<unsigned int>(sign) << 31)
|
||||
| 0x7F80'0000u // turn on all exponent bits
|
||||
| 0x7FC0'0000u // turn on all exponent bits and the qNaN bit
|
||||
| filteredPayload;
|
||||
// clang-format on
|
||||
|
||||
float resultConverted; // TRANSITION, bit_cast
|
||||
memcpy(&resultConverted, &result, sizeof(result));
|
||||
return resultConverted;
|
||||
return bit_cast<float>(result);
|
||||
}
|
||||
|
||||
template <>
|
||||
double mint_nan<double>(const bool sign, const unsigned long long payload) {
|
||||
const unsigned long long filteredPayload = payload & 0xF'FFFF'FFFF'FFFFllu; // bottom 52 bits
|
||||
constexpr double mint_nan<double>(const bool sign, const unsigned long long payload) {
|
||||
const unsigned long long filteredPayload = payload & 0x7'FFFF'FFFF'FFFFllu; // bottom 51 bits
|
||||
assert(filteredPayload == payload); // if this assert fails, payload didn't fit
|
||||
assert(filteredPayload != 0); // if this assert fails, the NaN would be an infinity
|
||||
|
||||
// clang-format off
|
||||
const unsigned long long result =
|
||||
(static_cast<unsigned long long>(sign) << 63)
|
||||
| 0x7FF0'0000'0000'0000u // turn on all exponent bits
|
||||
| 0x7FF8'0000'0000'0000u // turn on all exponent bits and the qNaN bit
|
||||
| filteredPayload;
|
||||
// clang-format on
|
||||
|
||||
double resultConverted; // TRANSITION, bit_cast
|
||||
memcpy(&resultConverted, &result, sizeof(result));
|
||||
return resultConverted;
|
||||
return bit_cast<double>(result);
|
||||
}
|
||||
|
||||
template <>
|
||||
long double mint_nan<long double>(const bool sign, const unsigned long long payload) {
|
||||
constexpr long double mint_nan<long double>(const bool sign, const unsigned long long payload) {
|
||||
return mint_nan<double>(sign, payload);
|
||||
}
|
||||
|
||||
|
@ -102,17 +149,36 @@ void assert_bitwise_equal(const Ty& a, const Ty& b) {
|
|||
assert(memcmp(&a, &b, sizeof(Ty)) == 0);
|
||||
}
|
||||
|
||||
// TRANSITION
|
||||
// numeric_limits<T>::signaling_NaN() doesn't work on x86 hosted MSVC
|
||||
// numeric_limits<float>::signaling_NaN() doesn't work on x64 hosted MSVC
|
||||
void make_snan(float& x) {
|
||||
constexpr unsigned int bits = 0x7f80'0001U;
|
||||
memcpy(&x, &bits, sizeof(x));
|
||||
}
|
||||
|
||||
void make_snan(double& x) {
|
||||
constexpr unsigned long long bits = 0x7ff0'0000'0000'0001ULL;
|
||||
memcpy(&x, &bits, sizeof(x));
|
||||
}
|
||||
|
||||
void make_snan(long double& x) {
|
||||
constexpr unsigned long long bits = 0x7ff0'0000'0000'0001ULL;
|
||||
memcpy(&x, &bits, sizeof(x));
|
||||
}
|
||||
|
||||
template <typename Ty>
|
||||
struct constants; // not defined
|
||||
|
||||
template <>
|
||||
struct constants<float> {
|
||||
static constexpr float TwoPlusUlp = 0x1.000002p+1f;
|
||||
static constexpr float OnePlusUlp = 0x1.000002p+0f;
|
||||
static constexpr float PointFivePlusUlp = 0x1.000002p-1f;
|
||||
static constexpr float OneMinusUlp = 0x1.fffffep-1f;
|
||||
static constexpr float NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr float NegOnePlusUlp = -OneMinusUlp;
|
||||
static constexpr float TwoPlusUlp = 0x1.000002p+1f;
|
||||
static constexpr float OnePlusUlp = 0x1.000002p+0f;
|
||||
static constexpr float PointFivePlusUlp = 0x1.000002p-1f;
|
||||
static constexpr float OneMinusUlp = 0x1.fffffep-1f;
|
||||
static constexpr float PointFiveMinusUlp = 0x1.fffffep-2f;
|
||||
static constexpr float NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr float NegOnePlusUlp = -OneMinusUlp;
|
||||
|
||||
static constexpr float EighthPlusUlp = 0x1.000002p-3f;
|
||||
static constexpr float EighthMinusUlp = 0x1.fffffep-4f;
|
||||
|
@ -120,12 +186,13 @@ struct constants<float> {
|
|||
|
||||
template <>
|
||||
struct constants<double> {
|
||||
static constexpr double TwoPlusUlp = 0x1.0000000000001p+1;
|
||||
static constexpr double OnePlusUlp = 0x1.0000000000001p+0;
|
||||
static constexpr double PointFivePlusUlp = 0x1.0000000000001p-1;
|
||||
static constexpr double OneMinusUlp = 0x1.fffffffffffffp-1;
|
||||
static constexpr double NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr double NegOnePlusUlp = -OneMinusUlp;
|
||||
static constexpr double TwoPlusUlp = 0x1.0000000000001p+1;
|
||||
static constexpr double OnePlusUlp = 0x1.0000000000001p+0;
|
||||
static constexpr double PointFivePlusUlp = 0x1.0000000000001p-1;
|
||||
static constexpr double OneMinusUlp = 0x1.fffffffffffffp-1;
|
||||
static constexpr double PointFiveMinusUlp = 0x1.fffffffffffffp-2;
|
||||
static constexpr double NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr double NegOnePlusUlp = -OneMinusUlp;
|
||||
|
||||
static constexpr double EighthPlusUlp = 0x1.0000000000001p-3;
|
||||
static constexpr double EighthMinusUlp = 0x1.fffffffffffffp-4;
|
||||
|
@ -133,12 +200,13 @@ struct constants<double> {
|
|||
|
||||
template <>
|
||||
struct constants<long double> {
|
||||
static constexpr long double TwoPlusUlp = 0x1.0000000000001p+1;
|
||||
static constexpr long double OnePlusUlp = 0x1.0000000000001p+0;
|
||||
static constexpr long double PointFivePlusUlp = 0x1.0000000000001p-1;
|
||||
static constexpr long double OneMinusUlp = 0x1.fffffffffffffp-1;
|
||||
static constexpr long double NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr long double NegOnePlusUlp = -OneMinusUlp;
|
||||
static constexpr long double TwoPlusUlp = 0x1.0000000000001p+1;
|
||||
static constexpr long double OnePlusUlp = 0x1.0000000000001p+0;
|
||||
static constexpr long double PointFivePlusUlp = 0x1.0000000000001p-1;
|
||||
static constexpr long double OneMinusUlp = 0x1.fffffffffffffp-1;
|
||||
static constexpr long double PointFiveMinusUlp = 0x1.fffffffffffffp-2;
|
||||
static constexpr long double NegOneMinusUlp = -OnePlusUlp;
|
||||
static constexpr long double NegOnePlusUlp = -OneMinusUlp;
|
||||
|
||||
static constexpr long double EighthPlusUlp = 0x1.0000000000001p-3;
|
||||
static constexpr long double EighthMinusUlp = 0x1.fffffffffffffp-4;
|
||||
|
@ -150,6 +218,7 @@ void test_constants() {
|
|||
assert(constants<Ty>::OnePlusUlp == nextafter(Ty(1.0), Ty(3.0)));
|
||||
assert(constants<Ty>::PointFivePlusUlp == nextafter(Ty(0.5), Ty(3.0)));
|
||||
assert(constants<Ty>::OneMinusUlp == nextafter(Ty(1.0), Ty(0.0)));
|
||||
assert(constants<Ty>::PointFiveMinusUlp == nextafter(Ty(0.5), Ty(-2.0)));
|
||||
assert(constants<Ty>::NegOneMinusUlp == nextafter(Ty(-1.0), Ty(-2.0)));
|
||||
assert(constants<Ty>::NegOnePlusUlp == nextafter(Ty(-1.0), Ty(0.0)));
|
||||
|
||||
|
@ -326,6 +395,7 @@ void test_midpoint_floating() {
|
|||
#ifdef _M_FP_STRICT
|
||||
{
|
||||
// test results exactly between 1 ULP:
|
||||
ExceptGuard except;
|
||||
RoundGuard round{FE_UPWARD};
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OnePlusUlp) == constants<Ty>::OnePlusUlp);
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OneMinusUlp) == Ty(1.0));
|
||||
|
@ -356,10 +426,13 @@ void test_midpoint_floating() {
|
|||
assert(midpoint(-limits<Ty>::min(), -limits<Ty>::max()) == -limits<Ty>::max() / 2);
|
||||
assert(midpoint(-limits<Ty>::denorm_min(), limits<Ty>::max()) == limits<Ty>::max() / 2);
|
||||
assert(midpoint(-limits<Ty>::denorm_min(), -limits<Ty>::max()) == -limits<Ty>::max() / 2);
|
||||
|
||||
assert(check_feexcept(0));
|
||||
}
|
||||
|
||||
// ditto for the other rounding modes:
|
||||
{
|
||||
ExceptGuard except;
|
||||
RoundGuard round{FE_DOWNWARD};
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OnePlusUlp) == Ty(1.0));
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OneMinusUlp) == constants<Ty>::OneMinusUlp);
|
||||
|
@ -394,9 +467,12 @@ void test_midpoint_floating() {
|
|||
== nextafter(limits<Ty>::max() / 2, limits<Ty>::lowest()));
|
||||
assert(midpoint(-limits<Ty>::denorm_min(), -limits<Ty>::max())
|
||||
== nextafter(-limits<Ty>::max() / 2, limits<Ty>::lowest()));
|
||||
|
||||
assert(check_feexcept(0));
|
||||
}
|
||||
|
||||
{
|
||||
ExceptGuard except;
|
||||
RoundGuard round{FE_TOWARDZERO};
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OnePlusUlp) == Ty(1.0));
|
||||
assert(midpoint(Ty(1.0), constants<Ty>::OneMinusUlp) == constants<Ty>::OneMinusUlp);
|
||||
|
@ -422,27 +498,49 @@ void test_midpoint_floating() {
|
|||
assert(midpoint(-limits<Ty>::min(), -limits<Ty>::max()) == -limits<Ty>::max() / 2);
|
||||
assert(midpoint(-limits<Ty>::denorm_min(), limits<Ty>::max()) == nextafter(limits<Ty>::max() / 2, Ty(0)));
|
||||
assert(midpoint(-limits<Ty>::denorm_min(), -limits<Ty>::max()) == -limits<Ty>::max() / 2);
|
||||
|
||||
assert(check_feexcept(0));
|
||||
}
|
||||
#endif // _M_FP_STRICT
|
||||
|
||||
assert(midpoint(limits<Ty>::denorm_min(), Ty(1.0)) == (limits<Ty>::denorm_min() + Ty(1.0)) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::max())
|
||||
== (limits<Ty>::denorm_min() + limits<Ty>::max()) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::lowest())
|
||||
== (limits<Ty>::denorm_min() + limits<Ty>::lowest()) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::infinity()) == limits<Ty>::infinity());
|
||||
assert(midpoint(limits<Ty>::denorm_min(), -limits<Ty>::infinity()) == -limits<Ty>::infinity());
|
||||
{
|
||||
ExceptGuard except;
|
||||
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), Ty(0)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(Ty(0), mint_nan<Ty>(0, 1)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), limits<Ty>::max()));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(limits<Ty>::max(), mint_nan<Ty>(0, 1)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), mint_nan<Ty>(0, 2)));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), Ty(1.0)) == (limits<Ty>::denorm_min() + Ty(1.0)) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::max())
|
||||
== (limits<Ty>::denorm_min() + limits<Ty>::max()) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::lowest())
|
||||
== (limits<Ty>::denorm_min() + limits<Ty>::lowest()) / Ty(2.0));
|
||||
assert(midpoint(limits<Ty>::denorm_min(), limits<Ty>::infinity()) == limits<Ty>::infinity());
|
||||
assert(midpoint(limits<Ty>::denorm_min(), -limits<Ty>::infinity()) == -limits<Ty>::infinity());
|
||||
|
||||
assert(isnan(midpoint(-limits<Ty>::infinity(), limits<Ty>::infinity())));
|
||||
assert(isnan(midpoint(limits<Ty>::quiet_NaN(), Ty(2.0))));
|
||||
assert(isnan(midpoint(Ty(2.0), limits<Ty>::quiet_NaN())));
|
||||
assert(isnan(midpoint(limits<Ty>::quiet_NaN(), limits<Ty>::quiet_NaN())));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), Ty(0)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(Ty(0), mint_nan<Ty>(0, 1)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), limits<Ty>::max()));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(limits<Ty>::max(), mint_nan<Ty>(0, 1)));
|
||||
assert_bitwise_equal(mint_nan<Ty>(0, 1), midpoint(mint_nan<Ty>(0, 1), mint_nan<Ty>(0, 1)));
|
||||
|
||||
assert(isnan(midpoint(limits<Ty>::quiet_NaN(), Ty(2.0))));
|
||||
assert(isnan(midpoint(Ty(2.0), limits<Ty>::quiet_NaN())));
|
||||
assert(isnan(midpoint(limits<Ty>::quiet_NaN(), limits<Ty>::quiet_NaN())));
|
||||
|
||||
assert(check_feexcept(0));
|
||||
}
|
||||
|
||||
// cases where midpoint() should raise FE_INVALID and return NaN
|
||||
constexpr auto test_midpoint_fe_invalid = [](const Ty& a, const Ty& b) {
|
||||
ExceptGuard except;
|
||||
const auto answer = midpoint(a, b);
|
||||
return check_feexcept(FE_INVALID) && isnan(answer);
|
||||
};
|
||||
|
||||
Ty snan;
|
||||
make_snan(snan);
|
||||
|
||||
assert(test_midpoint_fe_invalid(-limits<Ty>::infinity(), limits<Ty>::infinity()));
|
||||
assert(test_midpoint_fe_invalid(snan, limits<Ty>::quiet_NaN()));
|
||||
assert(test_midpoint_fe_invalid(limits<Ty>::quiet_NaN(), snan));
|
||||
assert(test_midpoint_fe_invalid(snan, snan));
|
||||
}
|
||||
|
||||
template <typename Ty>
|
||||
|
@ -496,7 +594,7 @@ constexpr bool test_midpoint_pointer() {
|
|||
}
|
||||
|
||||
template <typename Ty>
|
||||
int cmp(const Ty x, const Ty y) {
|
||||
constexpr int cmp(const Ty x, const Ty y) {
|
||||
if (x > y) {
|
||||
return 1;
|
||||
} else if (x < y) {
|
||||
|
@ -519,11 +617,12 @@ struct LerpNaNTestCase {
|
|||
Ty x;
|
||||
Ty y;
|
||||
Ty t;
|
||||
optional<Ty> expected_list[3] = {};
|
||||
};
|
||||
|
||||
template <typename Ty>
|
||||
struct LerpCases { // TRANSITION, VSO-934633
|
||||
static inline const LerpTestCase<Ty> lerpTestCases[] = {
|
||||
static inline constexpr LerpTestCase<Ty> lerpTestCases[] = {
|
||||
{Ty(-1.0), Ty(1.0), Ty(2.0), Ty(3.0)},
|
||||
{Ty(0.0), Ty(1.0), Ty(2.0), Ty(2.0)},
|
||||
{Ty(-1.0), Ty(0.0), Ty(2.0), Ty(1.0)},
|
||||
|
@ -564,8 +663,8 @@ struct LerpCases { // TRANSITION, VSO-934633
|
|||
// double: lerp(-0x0.0000000000001p-1022, -0x1.0000000000001p+0, 0.5) = -0x1.0000000000001p-1
|
||||
{-limits<Ty>::denorm_min(), constants<Ty>::NegOneMinusUlp, Ty(0.5), -constants<Ty>::PointFivePlusUlp},
|
||||
|
||||
{Ty(1.0), constants<Ty>::OnePlusUlp, nextafter(Ty(0.5), Ty(-1.0)), Ty(1.0)},
|
||||
{Ty(-1.0), constants<Ty>::NegOneMinusUlp, nextafter(Ty(0.5), Ty(-1.0)), Ty(-1.0)},
|
||||
{Ty(1.0), constants<Ty>::OnePlusUlp, constants<Ty>::PointFiveMinusUlp, Ty(1.0)},
|
||||
{Ty(-1.0), constants<Ty>::NegOneMinusUlp, constants<Ty>::PointFiveMinusUlp, Ty(-1.0)},
|
||||
|
||||
{Ty(1.0), constants<Ty>::OnePlusUlp, Ty(0.5), Ty(1.0)},
|
||||
{Ty(-1.0), constants<Ty>::NegOneMinusUlp, Ty(0.5), Ty(-1.0)},
|
||||
|
@ -586,8 +685,6 @@ struct LerpCases { // TRANSITION, VSO-934633
|
|||
{Ty(1.0), Ty(2.0), constants<Ty>::TwoPlusUlp, constants<Ty>::TwoPlusUlp + Ty(1.0)},
|
||||
{Ty(1.0), Ty(2.0), Ty(0.5), constants<Ty>::PointFivePlusUlp + Ty(1.0)},
|
||||
|
||||
{limits<Ty>::lowest(), limits<Ty>::max(), Ty(2.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::max(), limits<Ty>::lowest(), Ty(2.0), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::max(), limits<Ty>::max(), Ty(2.0), limits<Ty>::max()},
|
||||
{limits<Ty>::lowest(), limits<Ty>::lowest(), Ty(2.0), limits<Ty>::lowest()},
|
||||
|
||||
|
@ -598,84 +695,192 @@ struct LerpCases { // TRANSITION, VSO-934633
|
|||
{limits<Ty>::denorm_min(), limits<Ty>::infinity(), Ty(0.5), limits<Ty>::infinity()},
|
||||
{limits<Ty>::denorm_min(), -limits<Ty>::infinity(), Ty(0.5), -limits<Ty>::infinity()},
|
||||
|
||||
// the following handling of NaNs and infinities isn't in the spec, but seems like the right behavior:
|
||||
|
||||
// when the inputs are infinity and T is 1 or more, return the second parameter
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(1.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::infinity()},
|
||||
|
||||
// when the inputs are infinity and T is 0 or less, return the first parameter
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0), limits<Ty>::infinity()},
|
||||
|
||||
// if any of the inputs are NaN, return the first NaN
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729), mint_nan<Ty>(0, 42)},
|
||||
{Ty(1.0), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729), mint_nan<Ty>(1, 42)},
|
||||
{mint_nan<Ty>(1, 42), Ty(1.0), mint_nan<Ty>(0, 1729), mint_nan<Ty>(1, 42)},
|
||||
{Ty(1.0), Ty(1.0), mint_nan<Ty>(0, 1729), mint_nan<Ty>(0, 1729)},
|
||||
|
||||
{limits<Ty>::infinity(), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729), mint_nan<Ty>(1, 42)},
|
||||
{mint_nan<Ty>(1, 42), limits<Ty>::infinity(), mint_nan<Ty>(0, 1729), mint_nan<Ty>(1, 42)},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), mint_nan<Ty>(0, 1729), mint_nan<Ty>(0, 1729)},
|
||||
// the following handling of infinities isn't in the spec, but seems like the right behavior:
|
||||
|
||||
// if the values differ and T is an infinity, the appropriate infinity according to direction
|
||||
{Ty(0), Ty(1), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{Ty(0), Ty(1), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
|
||||
{Ty(0), -Ty(1), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{Ty(0), -Ty(1), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
|
||||
// if one of a or b is an infinity, choose the other value when t says exact, otherwise
|
||||
// return that infinity or the other according to "direction" of t
|
||||
{limits<Ty>::infinity(), Ty(1.0), Ty(1.0), Ty(1.0)},
|
||||
{limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OnePlusUlp, -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OneMinusUlp, limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
// when the inputs are infinity of the same sign and 0 < T < 1, return that infinity
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0.5), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0.5), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OneMinusUlp, limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OneMinusUlp, -limits<Ty>::infinity()},
|
||||
|
||||
// when the inputs are infinity of opposite signs and T > 1, return the second parameter
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(2.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(2.0), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
|
||||
// when the inputs are infinity of opposite signs and T < 0, return the first parameter
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -Ty(2.0), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -Ty(2.0), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
|
||||
// if a is an infinity, b is finite and T != 1, return that infinity or the other according to "direction" of t
|
||||
{limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), -Ty(1.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), -Ty(0.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), Ty(0.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), Ty(0.5), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OneMinusUlp, limits<Ty>::infinity()},
|
||||
|
||||
{-limits<Ty>::infinity(), Ty(1.0), Ty(1.0), Ty(1.0)},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OnePlusUlp, limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OneMinusUlp, -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OnePlusUlp, -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), Ty(2.0), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), Ty(1.0), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
|
||||
{-limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), -Ty(1.0), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), -Ty(0.0), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), Ty(0.0), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), Ty(0.5), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OneMinusUlp, -limits<Ty>::infinity()},
|
||||
|
||||
{Ty(1.0), limits<Ty>::infinity(), Ty(0.0), Ty(1.0)},
|
||||
{Ty(1.0), limits<Ty>::infinity(), -Ty(0.0), Ty(1.0)},
|
||||
{Ty(1.0), limits<Ty>::infinity(), limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), constants<Ty>::OnePlusUlp, limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), Ty(2.0), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
|
||||
// if b is an infinity, a is finite and T != 0, return that infinity or the other according to "direction" of t
|
||||
{Ty(1.0), limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), -limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), -Ty(1.0), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), -limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
|
||||
{Ty(1.0), -limits<Ty>::infinity(), Ty(0.0), Ty(1.0)},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -Ty(0.0), Ty(1.0)},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), Ty(0.5), limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), constants<Ty>::OneMinusUlp, limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), Ty(1.0), limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), Ty(2.0), limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{Ty(1.0), limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -limits<Ty>::max(), limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -Ty(1.0), limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -limits<Ty>::denorm_min(), limits<Ty>::infinity()},
|
||||
|
||||
// if all 3 values are infinities, choose the selected one according to t
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), limits<Ty>::denorm_min(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), Ty(0.5), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), constants<Ty>::OneMinusUlp, -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), Ty(1.0), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), constants<Ty>::OnePlusUlp, -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), Ty(2.0), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), limits<Ty>::max(), -limits<Ty>::infinity()},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
};
|
||||
|
||||
static inline constexpr LerpNaNTestCase<Ty> lerpNaNTestCases[] = {
|
||||
// when the inputs are infinity and T is between 0 and 1, return NaN
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::denorm_min()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::denorm_min()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OneMinusUlp},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OneMinusUlp},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0.5)},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0.5)},
|
||||
static inline constexpr LerpTestCase<Ty> lerpOverflowTestCases[] = {
|
||||
{limits<Ty>::lowest(), limits<Ty>::max(), Ty(2.0), limits<Ty>::infinity()},
|
||||
{limits<Ty>::max(), limits<Ty>::lowest(), Ty(2.0), -limits<Ty>::infinity()},
|
||||
};
|
||||
|
||||
static inline constexpr LerpNaNTestCase<Ty> lerpInvalidTestCases[] = {
|
||||
// if the values are equal and T is an infinity, NaN
|
||||
{Ty(0), Ty(0), limits<Ty>::infinity()},
|
||||
{Ty(0), Ty(0), -limits<Ty>::infinity()},
|
||||
|
||||
// when the inputs are infinity of the same sign and T <= 0, return NaN
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::infinity()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::max()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::max()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -Ty(1.0)},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -Ty(1.0)},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -limits<Ty>::denorm_min()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -limits<Ty>::denorm_min()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), -Ty(0.0)},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), -Ty(0.0)},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0.0)},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0.0)},
|
||||
|
||||
// when the inputs are infinity of the same sign and T >= 1, return NaN
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(1.0)},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(1.0)},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OnePlusUlp},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OnePlusUlp},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(2.0)},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(2.0)},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::max()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::max()},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
{-limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::infinity()},
|
||||
|
||||
// when the inputs are infinity of opposite signs and 0 <= T <= 1, return NaN
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), -Ty(0.0)},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), -Ty(0.0)},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0.0)},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0.0)},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), limits<Ty>::denorm_min()},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), limits<Ty>::denorm_min()},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(0.5)},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(0.5)},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), constants<Ty>::OneMinusUlp},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), constants<Ty>::OneMinusUlp},
|
||||
{-limits<Ty>::infinity(), limits<Ty>::infinity(), Ty(1.0)},
|
||||
{limits<Ty>::infinity(), -limits<Ty>::infinity(), Ty(1.0)},
|
||||
|
||||
// if a is an infinity, b is finite and T = 1, return NaN
|
||||
{limits<Ty>::infinity(), Ty(1.0), Ty(1.0)},
|
||||
{-limits<Ty>::infinity(), Ty(1.0), Ty(1.0)},
|
||||
|
||||
// if b is an infinity, a is finite and T = 0, return NaN
|
||||
{Ty(1.0), limits<Ty>::infinity(), Ty(0.0)},
|
||||
{Ty(1.0), limits<Ty>::infinity(), -Ty(0.0)},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), Ty(0.0)},
|
||||
{Ty(1.0), -limits<Ty>::infinity(), -Ty(0.0)},
|
||||
};
|
||||
|
||||
static inline constexpr LerpNaNTestCase<Ty> lerpNaNTestCases[] = {
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729),
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729)}},
|
||||
{Ty(1.0), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729), {mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729)}},
|
||||
{mint_nan<Ty>(1, 42), Ty(1.0), mint_nan<Ty>(0, 1729), {mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729)}},
|
||||
{Ty(1.0), Ty(1.0), mint_nan<Ty>(0, 1729), {mint_nan<Ty>(0, 1729)}},
|
||||
|
||||
{limits<Ty>::infinity(), mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729),
|
||||
{mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729)}},
|
||||
{mint_nan<Ty>(1, 42), limits<Ty>::infinity(), mint_nan<Ty>(0, 1729),
|
||||
{mint_nan<Ty>(1, 42), mint_nan<Ty>(0, 1729)}},
|
||||
{limits<Ty>::infinity(), limits<Ty>::infinity(), mint_nan<Ty>(0, 1729), {mint_nan<Ty>(0, 1729)}},
|
||||
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), -Ty(0.0), {mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42)}},
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), Ty(0.0), {mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42)}},
|
||||
{mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42), Ty(1.0), {mint_nan<Ty>(0, 42), mint_nan<Ty>(1, 42)}},
|
||||
{mint_nan<Ty>(0, 42), Ty(1.0), -Ty(0.0), {mint_nan<Ty>(0, 42)}},
|
||||
{mint_nan<Ty>(0, 42), Ty(1.0), Ty(0.0), {mint_nan<Ty>(0, 42)}},
|
||||
{mint_nan<Ty>(0, 42), Ty(1.0), Ty(1.0), {mint_nan<Ty>(0, 42)}},
|
||||
{mint_nan<Ty>(0, 42), limits<Ty>::infinity(), -Ty(0.0), {mint_nan<Ty>(0, 42)}},
|
||||
{mint_nan<Ty>(0, 42), limits<Ty>::infinity(), Ty(0.0), {mint_nan<Ty>(0, 42)}},
|
||||
{mint_nan<Ty>(0, 42), limits<Ty>::infinity(), Ty(1.0), {mint_nan<Ty>(0, 42)}},
|
||||
{Ty(1.0), mint_nan<Ty>(1, 42), -Ty(0.0), {mint_nan<Ty>(1, 42)}},
|
||||
{Ty(1.0), mint_nan<Ty>(1, 42), Ty(0.0), {mint_nan<Ty>(1, 42)}},
|
||||
{Ty(1.0), mint_nan<Ty>(1, 42), Ty(1.0), {mint_nan<Ty>(1, 42)}},
|
||||
{limits<Ty>::infinity(), mint_nan<Ty>(1, 42), -Ty(0.0), {mint_nan<Ty>(1, 42)}},
|
||||
{limits<Ty>::infinity(), mint_nan<Ty>(1, 42), Ty(0.0), {mint_nan<Ty>(1, 42)}},
|
||||
{limits<Ty>::infinity(), mint_nan<Ty>(1, 42), Ty(1.0), {mint_nan<Ty>(1, 42)}},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -730,25 +935,93 @@ bool test_lerp() {
|
|||
STATIC_ASSERT(is_signed_v<Ty>);
|
||||
STATIC_ASSERT(noexcept(lerp(Ty(), Ty(), Ty())));
|
||||
|
||||
// No constexpr tests for now because implementing constexpr lerp appears to depend on p0553, which was not accepted
|
||||
// (yet?).
|
||||
constexpr auto test_lerp_constexpr = [] {
|
||||
using bit_type = conditional_t<sizeof(Ty) == 4, unsigned int, unsigned long long>;
|
||||
|
||||
for (const auto& testCase : LerpCases<Ty>::lerpTestCases) {
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
assert(bit_cast<bit_type>(answer) == bit_cast<bit_type>(testCase.expected));
|
||||
}
|
||||
|
||||
for (auto&& testCase : LerpCases<Ty>::lerpNaNTestCases) {
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
assert(any_of(begin(testCase.expected_list), end(testCase.expected_list), [&](const auto& expected) {
|
||||
return expected.has_value() && bit_cast<bit_type>(answer) == bit_cast<bit_type>(expected.value());
|
||||
}));
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
STATIC_ASSERT(test_lerp_constexpr());
|
||||
|
||||
for (auto&& testCase : LerpCases<Ty>::lerpTestCases) {
|
||||
ExceptGuard except;
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
if (memcmp(&answer, &testCase.expected, sizeof(Ty)) != 0) {
|
||||
if (!check_feexcept(0) || memcmp(&answer, &testCase.expected, sizeof(Ty)) != 0) {
|
||||
print_lerp_result(testCase, answer);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& testCase : LerpCases<Ty>::lerpOverflowTestCases) {
|
||||
ExceptGuard except;
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
if (!check_feexcept(FE_OVERFLOW) || memcmp(&answer, &testCase.expected, sizeof(Ty)) != 0) {
|
||||
print_lerp_result(testCase, answer);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& testCase : LerpCases<Ty>::lerpInvalidTestCases) {
|
||||
ExceptGuard except;
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
if (!check_feexcept(FE_INVALID) || !isnan(answer)) {
|
||||
print_lerp_result(testCase, answer);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto&& testCase : LerpCases<Ty>::lerpNaNTestCases) {
|
||||
ExceptGuard except;
|
||||
const auto answer = lerp(testCase.x, testCase.y, testCase.t);
|
||||
if (!isnan(answer)) {
|
||||
if (!check_feexcept(0)
|
||||
|| none_of(begin(testCase.expected_list), end(testCase.expected_list), [&answer](const auto& expected) {
|
||||
return expected.has_value() && memcmp(&answer, &expected.value(), sizeof(Ty)) == 0;
|
||||
})) {
|
||||
print_lerp_result(testCase, answer);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto test_lerp_snan = [](const Ty& a, const Ty& b, const Ty& t) {
|
||||
ExceptGuard except;
|
||||
const auto answer = lerp(a, b, t);
|
||||
return check_feexcept(FE_INVALID) && isnan(answer);
|
||||
};
|
||||
|
||||
Ty snan;
|
||||
make_snan(snan);
|
||||
|
||||
assert(test_lerp_snan(snan, limits<Ty>::quiet_NaN(), limits<Ty>::quiet_NaN()));
|
||||
assert(test_lerp_snan(limits<Ty>::quiet_NaN(), snan, limits<Ty>::quiet_NaN()));
|
||||
assert(test_lerp_snan(snan, snan, limits<Ty>::quiet_NaN()));
|
||||
assert(test_lerp_snan(limits<Ty>::quiet_NaN(), limits<Ty>::quiet_NaN(), snan));
|
||||
assert(test_lerp_snan(snan, limits<Ty>::quiet_NaN(), snan));
|
||||
assert(test_lerp_snan(limits<Ty>::quiet_NaN(), snan, snan));
|
||||
assert(test_lerp_snan(snan, snan, snan));
|
||||
|
||||
assert(test_lerp_snan(Ty{0}, Ty{0}, snan));
|
||||
assert(test_lerp_snan(Ty{1}, Ty{1}, snan));
|
||||
assert(test_lerp_snan(Ty{0}, snan, Ty{0}));
|
||||
assert(test_lerp_snan(Ty{0}, snan, Ty{1}));
|
||||
assert(test_lerp_snan(snan, Ty{0}, Ty{0}));
|
||||
assert(test_lerp_snan(snan, Ty{0}, Ty{1}));
|
||||
|
||||
STATIC_ASSERT(cmp(lerp(Ty(1.0), Ty(2.0), Ty(4.0)), lerp(Ty(1.0), Ty(2.0), Ty(3.0))) * cmp(Ty(4.0), Ty(3.0))
|
||||
* cmp(Ty(2.0), Ty(1.0))
|
||||
>= 0);
|
||||
|
||||
assert(cmp(lerp(Ty(1.0), Ty(2.0), Ty(4.0)), lerp(Ty(1.0), Ty(2.0), Ty(3.0))) * cmp(Ty(4.0), Ty(3.0))
|
||||
* cmp(Ty(2.0), Ty(1.0))
|
||||
>= 0);
|
||||
|
@ -803,9 +1076,13 @@ int main() {
|
|||
STATIC_ASSERT(test_midpoint_floating_constexpr<double>());
|
||||
STATIC_ASSERT(test_midpoint_floating_constexpr<long double>());
|
||||
|
||||
test_midpoint_floating_constexpr<float>();
|
||||
test_midpoint_floating_constexpr<double>();
|
||||
test_midpoint_floating_constexpr<long double>();
|
||||
{
|
||||
ExceptGuard except;
|
||||
test_midpoint_floating_constexpr<float>();
|
||||
test_midpoint_floating_constexpr<double>();
|
||||
test_midpoint_floating_constexpr<long double>();
|
||||
assert(check_feexcept(0));
|
||||
}
|
||||
|
||||
test_midpoint_floating<float>();
|
||||
test_midpoint_floating<double>();
|
||||
|
|
|
@ -685,6 +685,20 @@ STATIC_ASSERT(__cpp_lib_integer_sequence == 201304L);
|
|||
STATIC_ASSERT(__cpp_lib_integral_constant_callable == 201304L);
|
||||
#endif
|
||||
|
||||
#if _HAS_CXX20
|
||||
#ifndef __cpp_lib_interpolate
|
||||
#error __cpp_lib_interpolate is not defined
|
||||
#elif __cpp_lib_interpolate != 201902L
|
||||
#error __cpp_lib_interpolate is not 201902L
|
||||
#else
|
||||
STATIC_ASSERT(__cpp_lib_interpolate == 201902L);
|
||||
#endif
|
||||
#else
|
||||
#ifdef __cpp_lib_interpolate
|
||||
#error __cpp_lib_interpolate is defined
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __cpp_lib_invoke
|
||||
#error __cpp_lib_invoke is not defined
|
||||
#elif __cpp_lib_invoke != 201411L
|
||||
|
|
Загрузка…
Ссылка в новой задаче