From f762d9d0a25e87a8f8133b0c512575d5ac08a00d Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 11 May 2021 12:08:16 +0700 Subject: [PATCH 01/28] Recalculate lerp if we got infinity. Eliminates some overflows. --- stl/inc/cmath | 14 ++++++++++++++ tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/stl/inc/cmath b/stl/inc/cmath index afcee6200d4..08aa1344131 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1330,6 +1330,20 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } + auto _Isinf = +[](_Ty val) constexpr { + // std::isinf is not constexpr so cannot use :( + return val == INFINITY || val == -INFINITY; + }; + auto _Signbit = +[](_Ty val) constexpr { + // std::signbit is not constexpr so cannot use :( + return val < 0; + }; + if (_Isinf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { + // we got inf but _ArgA has different sign, so try calculate again + // _ArgT is not zero because we got inf and _ArgA is finite. + return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); + } + return _Candidate; } diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index af6dedda278..60c7c50f56c 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1029,6 +1029,12 @@ bool test_lerp() { return true; } +void test_lerp_for_issue1917() { + using bit_type = unsigned long long; + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); +} + int main() { test_constants(); test_constants(); @@ -1099,4 +1105,6 @@ int main() { test_lerp(); test_lerp(); test_lerp(); + + test_lerp_for_issue1917(); } From 96ef790650157339645e7fd35ad85cb98f5ad6e4 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 11 May 2021 13:15:27 +0700 Subject: [PATCH 02/28] rename test method and change INFINITY to numeric_limits<_Ty>::infinity() --- stl/inc/cmath | 6 +++--- tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 08aa1344131..f207f68176c 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1330,11 +1330,11 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - auto _Isinf = +[](_Ty val) constexpr { + auto _Isinf = [](_Ty val) constexpr { // std::isinf is not constexpr so cannot use :( - return val == INFINITY || val == -INFINITY; + return val == _STD numeric_limits<_Ty>::infinity() || val == -_STD numeric_limits<_Ty>::infinity(); }; - auto _Signbit = +[](_Ty val) constexpr { + auto _Signbit = [](_Ty val) constexpr { // std::signbit is not constexpr so cannot use :( return val < 0; }; diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 60c7c50f56c..483a626340b 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1029,7 +1029,7 @@ bool test_lerp() { return true; } -void test_lerp_for_issue1917() { +void test_GH_1917() { using bit_type = unsigned long long; assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); @@ -1106,5 +1106,5 @@ int main() { test_lerp(); test_lerp(); - test_lerp_for_issue1917(); + test_GH_1917(); } From 337fc819a39f0599a2ad9f0f3dc617735004b242 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 11 May 2021 13:58:14 +0700 Subject: [PATCH 03/28] fix more tests --- stl/inc/cmath | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index f207f68176c..1d3cada997e 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,6 +23,7 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 +#include #include #endif // _HAS_CXX20 @@ -1330,18 +1331,20 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - auto _Isinf = [](_Ty val) constexpr { - // std::isinf is not constexpr so cannot use :( - return val == _STD numeric_limits<_Ty>::infinity() || val == -_STD numeric_limits<_Ty>::infinity(); - }; - auto _Signbit = [](_Ty val) constexpr { - // std::signbit is not constexpr so cannot use :( - return val < 0; - }; - if (_Isinf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { - // we got inf but _ArgA has different sign, so try calculate again - // _ArgT is not zero because we got inf and _ArgA is finite. - return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); + if constexpr (_STD numeric_limits<_Ty>::has_infinity) { + auto _Isinf = [](_Ty val) constexpr { + // std::isinf is not constexpr so cannot use :( + return val == _STD numeric_limits<_Ty>::infinity() || val == -_STD numeric_limits<_Ty>::infinity(); + }; + auto _Signbit = [](_Ty val) constexpr { + // std::signbit is not constexpr so cannot use :( + return val < 0; + }; + if (_Isinf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { + // we got inf but _ArgA has different sign, so try calculate again + // _ArgT is not zero because we got inf and _ArgA is finite. + return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); + } } return _Candidate; From 4cb06efba9984f772b45c4dbafb215d16c3b6dfb Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 11 May 2021 14:05:17 +0700 Subject: [PATCH 04/28] remove unneeded _STD Co-authored-by: Michael Schellenberger Costa --- stl/inc/cmath | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 1d3cada997e..131b6d04ba9 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1331,10 +1331,10 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - if constexpr (_STD numeric_limits<_Ty>::has_infinity) { + if constexpr (numeric_limits<_Ty>::has_infinity) { auto _Isinf = [](_Ty val) constexpr { // std::isinf is not constexpr so cannot use :( - return val == _STD numeric_limits<_Ty>::infinity() || val == -_STD numeric_limits<_Ty>::infinity(); + return val == numeric_limits<_Ty>::infinity() || val == -numeric_limits<_Ty>::infinity(); }; auto _Signbit = [](_Ty val) constexpr { // std::signbit is not constexpr so cannot use :( From 1d4c2909f1134ee3d681c2d565632427fc2f8c5a Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 11 May 2021 14:35:44 +0700 Subject: [PATCH 05/28] stl already has constexpr isinf Co-authored-by: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/cmath | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 131b6d04ba9..12542ce34c8 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,7 +23,6 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 -#include #include #endif // _HAS_CXX20 @@ -1331,20 +1330,14 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - if constexpr (numeric_limits<_Ty>::has_infinity) { - auto _Isinf = [](_Ty val) constexpr { - // std::isinf is not constexpr so cannot use :( - return val == numeric_limits<_Ty>::infinity() || val == -numeric_limits<_Ty>::infinity(); - }; - auto _Signbit = [](_Ty val) constexpr { - // std::signbit is not constexpr so cannot use :( - return val < 0; - }; - if (_Isinf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { - // we got inf but _ArgA has different sign, so try calculate again - // _ArgT is not zero because we got inf and _ArgA is finite. - return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); - } + auto _Signbit = [](_Ty val) constexpr { + // std::signbit is not constexpr so cannot use :( + return val < 0; + }; + if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { + // we got inf but _ArgA has different sign, so try calculate again + // _ArgT is not zero because we got inf and _ArgA is finite. + return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); } return _Candidate; From bbfb0603447dcfba809392b39da04794809e97af Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 14:20:56 +0700 Subject: [PATCH 06/28] Clear FE_OVERFLOW exception flag --- stl/inc/cmath | 16 ++++++++++++++++ tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 1 + 2 files changed, 17 insertions(+) diff --git a/stl/inc/cmath b/stl/inc/cmath index 12542ce34c8..a4b687ef0b3 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,9 +23,16 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 +#ifdef _M_FP_STRICT +#include +#endif #include #endif // _HAS_CXX20 +#ifdef _M_FP_STRICT +#pragma fenv_access(on) +#endif + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1330,12 +1337,21 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } + auto _Clear_FE_OVERFLOW_flag = []() constexpr { +#ifdef _M_FP_STRICT + if (!_STD is_constant_evaluated()) { + feclearexcept(FE_OVERFLOW); + } +#endif + }; + auto _Signbit = [](_Ty val) constexpr { // std::signbit is not constexpr so cannot use :( return val < 0; }; if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { // we got inf but _ArgA has different sign, so try calculate again + _Clear_FE_OVERFLOW_flag(); // _ArgT is not zero because we got inf and _ArgA is finite. return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); } diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 483a626340b..6d8dbb6cc79 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1087,6 +1087,7 @@ int main() { test_midpoint_floating_constexpr(); test_midpoint_floating_constexpr(); test_midpoint_floating_constexpr(); + test_GH_1917(); assert(check_feexcept(0)); } From 13c578f7c107182d3eee4ecc8eed2aa5976dd61e Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 14:25:02 +0700 Subject: [PATCH 07/28] include instead of --- stl/inc/cmath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index a4b687ef0b3..39c65a3afed 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -24,7 +24,7 @@ #if _HAS_CXX20 #ifdef _M_FP_STRICT -#include +#include #endif #include #endif // _HAS_CXX20 From 3e7ab76f700773e31a0318480a33894a75d5ec18 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 14:42:30 +0700 Subject: [PATCH 08/28] Check different rounding modes --- stl/inc/cmath | 13 +++++++- .../std/tests/P0811R3_midpoint_lerp/test.cpp | 31 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 39c65a3afed..6916b6f6bf3 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1337,6 +1337,17 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } + auto _Is_Overflow = [](_Ty val) constexpr { +#ifdef _M_FP_STRICT + if (_STD is_constant_evaluated()) { + return _Is_inf(val); + } + return fetestexcept(FE_OVERFLOW) != 0; +#else + return _Is_inf(val); +#endif + }; + auto _Clear_FE_OVERFLOW_flag = []() constexpr { #ifdef _M_FP_STRICT if (!_STD is_constant_evaluated()) { @@ -1349,7 +1360,7 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T // std::signbit is not constexpr so cannot use :( return val < 0; }; - if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { + if (_Is_Overflow(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { // we got inf but _ArgA has different sign, so try calculate again _Clear_FE_OVERFLOW_flag(); // _ArgT is not zero because we got inf and _ArgA is finite. diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 6d8dbb6cc79..6f76b3484a8 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1031,8 +1031,36 @@ bool test_lerp() { void test_GH_1917() { using bit_type = unsigned long long; - assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + { + ExceptGuard except; + + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + assert(check_feexcept(0)); + } +#ifdef _M_FP_STRICT + { + ExceptGuard except; + RoundGuard round{FE_UPWARD}; + + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + assert(check_feexcept(0)); + } + { + ExceptGuard except; + RoundGuard round{FE_DOWNWARD}; + + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + assert(check_feexcept(0)); + } + { + ExceptGuard except; + RoundGuard round{FE_TOWARDZERO}; + + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + assert(check_feexcept(0)); + } +#endif } int main() { @@ -1087,7 +1115,6 @@ int main() { test_midpoint_floating_constexpr(); test_midpoint_floating_constexpr(); test_midpoint_floating_constexpr(); - test_GH_1917(); assert(check_feexcept(0)); } From 7384454ed02cce1f56fe100f0e6c5cfe8365067a Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 15:02:55 +0700 Subject: [PATCH 09/28] add some comments --- stl/inc/cmath | 13 ++++++++----- tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 6916b6f6bf3..3e654b408c7 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -25,13 +25,16 @@ #if _HAS_CXX20 #ifdef _M_FP_STRICT #include -#endif +#endif // _M_FP_STRICT #include #endif // _HAS_CXX20 +#if _HAS_CXX20 #ifdef _M_FP_STRICT +// TRANSITION, VSO-923474 -- should be #pragma STDC FENV_ACCESS ON #pragma fenv_access(on) -#endif +#endif // _M_FP_STRICT +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -1343,9 +1346,9 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T return _Is_inf(val); } return fetestexcept(FE_OVERFLOW) != 0; -#else +#else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv return _Is_inf(val); -#endif +#endif // _M_FP_STRICT }; auto _Clear_FE_OVERFLOW_flag = []() constexpr { @@ -1353,7 +1356,7 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T if (!_STD is_constant_evaluated()) { feclearexcept(FE_OVERFLOW); } -#endif +#endif // _M_FP_STRICT }; auto _Signbit = [](_Ty val) constexpr { diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 6f76b3484a8..76f65937900 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1060,7 +1060,7 @@ void test_GH_1917() { assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(0)); } -#endif +#endif // _M_FP_STRICT } int main() { From 18fd0edc0dc6f7f14a6300607c78a0f1a1173bd8 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 22:06:01 +0700 Subject: [PATCH 10/28] Exception flags could have already been set before calling lerp Co-authored-by: statementreply --- stl/inc/cmath | 34 +++++++++++++++++++ .../std/tests/P0811R3_midpoint_lerp/test.cpp | 8 +++++ 2 files changed, 42 insertions(+) diff --git a/stl/inc/cmath b/stl/inc/cmath index 3e654b408c7..0761cde0f7d 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1314,6 +1314,40 @@ template _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 +#ifdef _M_FP_STRICT + class _ExceptGuard { + public: + constexpr _ExceptGuard() { + if (!_STD is_constant_evaluated()) { + [[maybe_unused]] const int holdExcept = feholdexcept(&env); + } + } + + _ExceptGuard(const _ExceptGuard&) = delete; + _ExceptGuard& operator=(const _ExceptGuard&) = delete; + + constexpr ~_ExceptGuard() { + if (!_STD is_constant_evaluated()) { + [[maybe_unused]] const int updateEnv = feupdateenv(&env); + } + } + + private: + fenv_t env; + }; +#else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv + class _ExceptGuard { + public: + constexpr _ExceptGuard() {} + + _ExceptGuard(const _ExceptGuard&) = delete; + _ExceptGuard& operator=(const _ExceptGuard&) = delete; + + constexpr ~_ExceptGuard() {} + }; +#endif + _ExceptGuard _Guard; + const bool _T_is_finite = _Is_finite(_ArgT); if (_T_is_finite && _Is_finite(_ArgA) && _Is_finite(_ArgB)) { // 99% case, put it first; this block comes from P0811R3 diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 76f65937900..973afc905ba 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1060,6 +1060,14 @@ void test_GH_1917() { assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(0)); } + { + ExceptGuard except; + int r = feraiseexcept(FE_OVERFLOW); + + assert(r == 0); + assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); + assert(check_feexcept(FE_OVERFLOW)); + } #endif // _M_FP_STRICT } From c06a9d369973a639c8c1f50d007c51ecccc46442 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 12 May 2021 22:43:08 +0700 Subject: [PATCH 11/28] use default ctor and dtor --- stl/inc/cmath | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 0761cde0f7d..235e1fb2ac6 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1338,15 +1338,15 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T #else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv class _ExceptGuard { public: - constexpr _ExceptGuard() {} + constexpr _ExceptGuard() = default; _ExceptGuard(const _ExceptGuard&) = delete; _ExceptGuard& operator=(const _ExceptGuard&) = delete; - constexpr ~_ExceptGuard() {} + constexpr ~_ExceptGuard() = default; }; #endif - _ExceptGuard _Guard; + [[maybe_unused]] _ExceptGuard _Guard; const bool _T_is_finite = _Is_finite(_ArgT); if (_T_is_finite && _Is_finite(_ArgA) && _Is_finite(_ArgB)) { From c5404ee190ab8fb1ae62be281ae96d0fe8df6c1e Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 13 May 2021 10:12:00 +0700 Subject: [PATCH 12/28] use existing code for constexpr signbit Co-authored-by: Curtis J Bezault --- stl/inc/cmath | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 235e1fb2ac6..9df4f7ccd5b 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -26,6 +26,7 @@ #ifdef _M_FP_STRICT #include #endif // _M_FP_STRICT +#include #include #endif // _HAS_CXX20 @@ -1394,9 +1395,12 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T }; auto _Signbit = [](_Ty val) constexpr { - // std::signbit is not constexpr so cannot use :( - return val < 0; + using _Float_bits_t = typename _STD _Floating_type_traits<_Ty>::_Uint_type; + constexpr _Float_bits_t _Sign_mask_v = _STD _Floating_type_traits<_Ty>::_Shifted_sign_mask; + const auto _Bits = _Bit_cast<_Float_bits_t>(val); + return (_Bits & _Sign_mask_v) != 0; }; + if (_Is_Overflow(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { // we got inf but _ArgA has different sign, so try calculate again _Clear_FE_OVERFLOW_flag(); From d82177b574d06dcdf3d9ca059e8d815d17ec6a07 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 13 May 2021 11:19:41 +0700 Subject: [PATCH 13/28] use _Pretty variable names --- stl/inc/cmath | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 9df4f7ccd5b..f821e4eb4bd 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1320,7 +1320,7 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T public: constexpr _ExceptGuard() { if (!_STD is_constant_evaluated()) { - [[maybe_unused]] const int holdExcept = feholdexcept(&env); + [[maybe_unused]] const int _HoldExcept = feholdexcept(&_Env); } } @@ -1329,12 +1329,12 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T constexpr ~_ExceptGuard() { if (!_STD is_constant_evaluated()) { - [[maybe_unused]] const int updateEnv = feupdateenv(&env); + [[maybe_unused]] const int _UpdateEnv = feupdateenv(&_Env); } } private: - fenv_t env; + fenv_t _Env; }; #else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv class _ExceptGuard { From 6bc971ea8be0755808445b70f4be211d3fa0006b Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 13 May 2021 19:10:46 +0700 Subject: [PATCH 14/28] use fma Co-authored-by: Alexander Bolz --- stl/inc/cmath | 83 +++++++++------------------------------------------ 1 file changed, 14 insertions(+), 69 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index f821e4eb4bd..80c14651143 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,20 +23,10 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 -#ifdef _M_FP_STRICT -#include -#endif // _M_FP_STRICT #include #include #endif // _HAS_CXX20 -#if _HAS_CXX20 -#ifdef _M_FP_STRICT -// TRANSITION, VSO-923474 -- should be #pragma STDC FENV_ACCESS ON -#pragma fenv_access(on) -#endif // _M_FP_STRICT -#endif // _HAS_CXX20 - #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1315,40 +1305,6 @@ template _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 -#ifdef _M_FP_STRICT - class _ExceptGuard { - public: - constexpr _ExceptGuard() { - if (!_STD is_constant_evaluated()) { - [[maybe_unused]] const int _HoldExcept = feholdexcept(&_Env); - } - } - - _ExceptGuard(const _ExceptGuard&) = delete; - _ExceptGuard& operator=(const _ExceptGuard&) = delete; - - constexpr ~_ExceptGuard() { - if (!_STD is_constant_evaluated()) { - [[maybe_unused]] const int _UpdateEnv = feupdateenv(&_Env); - } - } - - private: - fenv_t _Env; - }; -#else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv - class _ExceptGuard { - public: - constexpr _ExceptGuard() = default; - - _ExceptGuard(const _ExceptGuard&) = delete; - _ExceptGuard& operator=(const _ExceptGuard&) = delete; - - constexpr ~_ExceptGuard() = default; - }; -#endif - [[maybe_unused]] _ExceptGuard _Guard; - const bool _T_is_finite = _Is_finite(_ArgT); if (_T_is_finite && _Is_finite(_ArgA) && _Is_finite(_ArgB)) { // 99% case, put it first; this block comes from P0811R3 @@ -1363,7 +1319,14 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } // exact at _ArgT == 0, monotonic except near _ArgT == 1, bounded, determinate, and consistent: - const auto _Candidate = _ArgA + _ArgT * (_ArgB - _ArgA); + const auto _Candidate = [=]() constexpr { + if (_STD is_constant_evaluated()) { + return _ArgA + _ArgT * (_ArgB - _ArgA); + } + return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); + } + (); + // monotonic near _ArgT == 1: if ((_ArgT > 1) == (_ArgB > _ArgA)) { if (_ArgB > _Candidate) { @@ -1375,25 +1338,6 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - auto _Is_Overflow = [](_Ty val) constexpr { -#ifdef _M_FP_STRICT - if (_STD is_constant_evaluated()) { - return _Is_inf(val); - } - return fetestexcept(FE_OVERFLOW) != 0; -#else // ^^^ defined(_M_FP_STRICT) / !defined(_M_FP_STRICT) vvv - return _Is_inf(val); -#endif // _M_FP_STRICT - }; - - auto _Clear_FE_OVERFLOW_flag = []() constexpr { -#ifdef _M_FP_STRICT - if (!_STD is_constant_evaluated()) { - feclearexcept(FE_OVERFLOW); - } -#endif // _M_FP_STRICT - }; - auto _Signbit = [](_Ty val) constexpr { using _Float_bits_t = typename _STD _Floating_type_traits<_Ty>::_Uint_type; constexpr _Float_bits_t _Sign_mask_v = _STD _Floating_type_traits<_Ty>::_Shifted_sign_mask; @@ -1401,11 +1345,12 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T return (_Bits & _Sign_mask_v) != 0; }; - if (_Is_Overflow(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { - // we got inf but _ArgA has different sign, so try calculate again - _Clear_FE_OVERFLOW_flag(); - // _ArgT is not zero because we got inf and _ArgA is finite. - return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); + if (_STD is_constant_evaluated()) { + if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { + // we got inf but _ArgA has different sign, so try calculate again + // _ArgT is not zero because we got inf and _ArgA is finite. + return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); + } } return _Candidate; From c202089a0840a71a6e3e1f67d2a95892e94625af Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 08:40:42 +0700 Subject: [PATCH 15/28] use named functions instead of lambdas to reduce duplication between translation units --- stl/inc/cmath | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 80c14651143..a4e462ad0d2 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1301,6 +1301,22 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { #if _HAS_CXX20 // FUNCTION lerp +template +_NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) { + if (_STD is_constant_evaluated()) { + return _ArgA + _ArgT * (_ArgB - _ArgA); + } + return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); +} + +template +_NODISCARD constexpr bool _Signbit(const _Ty val) { + using _Float_bits_t = typename _STD _Floating_type_traits<_Ty>::_Uint_type; + constexpr _Float_bits_t _Sign_mask_v = _STD _Floating_type_traits<_Ty>::_Shifted_sign_mask; + const auto _Bits = _Bit_cast<_Float_bits_t>(val); + return (_Bits & _Sign_mask_v) != 0; +} + template _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 @@ -1319,13 +1335,7 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } // exact at _ArgT == 0, monotonic except near _ArgT == 1, bounded, determinate, and consistent: - const auto _Candidate = [=]() constexpr { - if (_STD is_constant_evaluated()) { - return _ArgA + _ArgT * (_ArgB - _ArgA); - } - return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); - } - (); + const auto _Candidate = _Linear_for_lerp(_ArgA, _ArgB, _ArgT); // monotonic near _ArgT == 1: if ((_ArgT > 1) == (_ArgB > _ArgA)) { @@ -1338,13 +1348,6 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - auto _Signbit = [](_Ty val) constexpr { - using _Float_bits_t = typename _STD _Floating_type_traits<_Ty>::_Uint_type; - constexpr _Float_bits_t _Sign_mask_v = _STD _Floating_type_traits<_Ty>::_Shifted_sign_mask; - const auto _Bits = _Bit_cast<_Float_bits_t>(val); - return (_Bits & _Sign_mask_v) != 0; - }; - if (_STD is_constant_evaluated()) { if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { // we got inf but _ArgA has different sign, so try calculate again From 66c681832016c784166d0257804d993a2fdaecf0 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 08:53:01 +0700 Subject: [PATCH 16/28] Move logic into _Linear_for_lerp. Don't overflow in constant expression. Co-authored-by: Matt Stephanson --- stl/inc/cmath | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index a4e462ad0d2..47ddd659e17 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1304,7 +1304,20 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { template _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) { if (_STD is_constant_evaluated()) { - return _ArgA + _ArgT * (_ArgB - _ArgA); + auto _Smaller = _ArgT; + auto _Larger = _ArgB - _ArgA; + auto _Abs_smaller = _Float_abs(_Smaller); + auto _Abs_larger = _Float_abs(_Larger); + if (_Abs_larger < _Abs_smaller) { + _STD swap(_Smaller, _Larger); + _STD swap(_Abs_smaller, _Abs_larger); + } + + if (_Abs_smaller > 1 && _Abs_larger > numeric_limits<_Ty>::max() / _Abs_larger) { + return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); + } else { + return _ArgA + _Smaller * _Larger; + } } return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); } @@ -1348,14 +1361,6 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T } } - if (_STD is_constant_evaluated()) { - if (_Is_inf(_Candidate) && (_Signbit(_Candidate) != _Signbit(_ArgA))) { - // we got inf but _ArgA has different sign, so try calculate again - // _ArgT is not zero because we got inf and _ArgA is finite. - return _ArgT * (_ArgB - _ArgA + _ArgA / _ArgT); - } - } - return _Candidate; } From 6e18a9df438fe6af00bdc9e25b97199553f071a5 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 09:18:03 +0700 Subject: [PATCH 17/28] fix collision with macro 'max' --- stl/inc/cmath | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 47ddd659e17..a723d32243d 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1313,7 +1313,7 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > numeric_limits<_Ty>::max() / _Abs_larger) { + if (_Abs_smaller > 1 && _Abs_larger > (numeric_limits<_Ty>::max)() / _Abs_larger) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; From 30b4a68fe580eaa8dab34ae0c82f70401ab62a3f Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 09:51:27 +0700 Subject: [PATCH 18/28] add missing `#include ` --- stl/inc/cmath | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/cmath b/stl/inc/cmath index a723d32243d..e540aebc5d9 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,6 +23,7 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 +#include #include #include #endif // _HAS_CXX20 From 672b38086de7eb7a0f412ac93c2bdd5df84a9c77 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 10:25:36 +0700 Subject: [PATCH 19/28] we don't use _Signbit anymore --- stl/inc/cmath | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index e540aebc5d9..849071d733c 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1320,15 +1320,8 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons return _ArgA + _Smaller * _Larger; } } - return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); -} -template -_NODISCARD constexpr bool _Signbit(const _Ty val) { - using _Float_bits_t = typename _STD _Floating_type_traits<_Ty>::_Uint_type; - constexpr _Float_bits_t _Sign_mask_v = _STD _Floating_type_traits<_Ty>::_Shifted_sign_mask; - const auto _Bits = _Bit_cast<_Float_bits_t>(val); - return (_Bits & _Sign_mask_v) != 0; + return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA); } template @@ -1350,7 +1343,6 @@ _NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _T // exact at _ArgT == 0, monotonic except near _ArgT == 1, bounded, determinate, and consistent: const auto _Candidate = _Linear_for_lerp(_ArgA, _ArgB, _ArgT); - // monotonic near _ArgT == 1: if ((_ArgT > 1) == (_ArgB > _ArgA)) { if (_ArgB > _Candidate) { From e71e8837aa305857bb149c5b80c1ccb2ef753b6a Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sun, 4 Jul 2021 10:29:17 +0700 Subject: [PATCH 20/28] we don't use anymore --- stl/inc/cmath | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 849071d733c..e1971d75403 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -24,7 +24,6 @@ #if _HAS_CXX20 #include -#include #include #endif // _HAS_CXX20 From b2b1b2b1d4e0e9c5253cadd9541082c7fbec2f92 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 5 Jul 2021 23:38:54 +0700 Subject: [PATCH 21/28] use `` instead of `` --- stl/inc/cmath | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index e1971d75403..e8a336a7c07 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,7 +23,7 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 -#include +#include #include #endif // _HAS_CXX20 @@ -1301,6 +1301,24 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { #if _HAS_CXX20 // FUNCTION lerp +template +_Ty _Floating_max(); + +template <> +_NODISCARD constexpr float _Floating_max() { + return FLT_MAX; +} + +template <> +_NODISCARD constexpr double _Floating_max() { + return DBL_MAX; +} + +template <> +_NODISCARD constexpr long double _Floating_max() { + return LDBL_MAX; +} + template _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) { if (_STD is_constant_evaluated()) { @@ -1313,7 +1331,7 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > (numeric_limits<_Ty>::max)() / _Abs_larger) { + if (_Abs_smaller > 1 && _Abs_larger > _Floating_max<_Ty>() / _Abs_larger) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; From 43584bfb97f8a63dd76ae49edc11cf21a0c1c6e6 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Mon, 5 Jul 2021 23:55:54 +0700 Subject: [PATCH 22/28] use remove_cv_t and actual header is `` --- stl/inc/cmath | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index e8a336a7c07..d757e34cd5f 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,7 +23,8 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 -#include +#include +#include #include #endif // _HAS_CXX20 @@ -1331,7 +1332,7 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > _Floating_max<_Ty>() / _Abs_larger) { + if (_Abs_smaller > 1 && _Abs_larger > _Floating_max>() / _Abs_larger) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; From b288a58e39454508cc56666fd2e54315f9b55d4a Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 6 Jul 2021 00:27:04 +0700 Subject: [PATCH 23/28] use variable templates --- stl/inc/cmath | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index d757e34cd5f..42c0aa578eb 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1303,22 +1303,16 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { #if _HAS_CXX20 // FUNCTION lerp template -_Ty _Floating_max(); +inline constexpr _Ty _Floating_max; template <> -_NODISCARD constexpr float _Floating_max() { - return FLT_MAX; -} +inline constexpr float _Floating_max = FLT_MAX; template <> -_NODISCARD constexpr double _Floating_max() { - return DBL_MAX; -} +inline constexpr double _Floating_max = DBL_MAX; template <> -_NODISCARD constexpr long double _Floating_max() { - return LDBL_MAX; -} +inline constexpr long double _Floating_max = LDBL_MAX; template _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) { @@ -1332,7 +1326,7 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > _Floating_max>() / _Abs_larger) { + if (_Abs_smaller > 1 && _Abs_larger > _Floating_max> / _Abs_larger) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; From be88a0d009f80b64cdad9d253826ebe76b2e8349 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sat, 18 Dec 2021 22:19:48 +0700 Subject: [PATCH 24/28] add short description for test --- tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index b0fdaab658e..475be3b96c1 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1024,6 +1024,7 @@ bool test_lerp() { } void test_GH_1917() { + // : lerp(1e+308, 5e+307, 4.0) spuriously overflows using bit_type = unsigned long long; STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); { From 838cf8fc875be5101bfda19026dbef8191ef551c Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Sat, 7 May 2022 19:51:57 +0700 Subject: [PATCH 25/28] change test name --- tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 26fcf03925a..f44c0e4510b 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1023,7 +1023,7 @@ bool test_lerp() { return true; } -void test_GH_1917() { +void test_gh_1917() { // GH-1917 : lerp(1e+308, 5e+307, 4.0) spuriously overflows using bit_type = unsigned long long; STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); @@ -1151,7 +1151,7 @@ int main() { test_lerp(); test_lerp(); - test_GH_1917(); + test_gh_1917(); test_gh_2112(); STATIC_ASSERT(test_gh_2112()); } From 9f2d248a03ec1c86a59c9dd9410e33c2904500b2 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Thu, 16 Jun 2022 00:28:20 +0700 Subject: [PATCH 26/28] add a test for `float` and improve the estimate of the inequality --- stl/inc/cmath | 2 +- .../std/tests/P0811R3_midpoint_lerp/test.cpp | 41 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 37a3ccfa4ae..33e4783fda1 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1361,7 +1361,7 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > _Floating_max> / _Abs_larger) { + if (_Abs_smaller > 1 && _Abs_larger > _Floating_max> / _Abs_smaller) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index f44c0e4510b..da12486f640 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1025,7 +1025,8 @@ bool test_lerp() { void test_gh_1917() { // GH-1917 : lerp(1e+308, 5e+307, 4.0) spuriously overflows - using bit_type = unsigned long long; + using bit_type = unsigned long long; + using float_bit_type = unsigned int; STATIC_ASSERT(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); { ExceptGuard except; @@ -1033,6 +1034,13 @@ void test_gh_1917() { assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(0)); } + STATIC_ASSERT(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + { + ExceptGuard except; + + assert(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + assert(check_feexcept(0)); + } #ifdef _M_FP_STRICT { ExceptGuard except; @@ -1041,6 +1049,13 @@ void test_gh_1917() { assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(0)); } + { + ExceptGuard except; + RoundGuard round{FE_UPWARD}; + + assert(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + assert(check_feexcept(0)); + } { ExceptGuard except; RoundGuard round{FE_DOWNWARD}; @@ -1048,6 +1063,13 @@ void test_gh_1917() { assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(0)); } + { + ExceptGuard except; + RoundGuard round{FE_DOWNWARD}; + + assert(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + assert(check_feexcept(0)); + } { ExceptGuard except; RoundGuard round{FE_TOWARDZERO}; @@ -1057,12 +1079,27 @@ void test_gh_1917() { } { ExceptGuard except; - int r = feraiseexcept(FE_OVERFLOW); + RoundGuard round{FE_TOWARDZERO}; + + assert(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + assert(check_feexcept(0)); + } + { + ExceptGuard except; + const int r = feraiseexcept(FE_OVERFLOW); assert(r == 0); assert(bit_cast(lerp(1e+308, 5e+307, 4.0)) == bit_cast(-1e+308)); assert(check_feexcept(FE_OVERFLOW)); } + { + ExceptGuard except; + const int r = feraiseexcept(FE_OVERFLOW); + + assert(r == 0); + assert(bit_cast(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast(-2e+38f)); + assert(check_feexcept(FE_OVERFLOW)); + } #endif // _M_FP_STRICT } From f9861025f1fda4b56f2f80e028efecfb490de807 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 17 Jun 2022 15:31:14 +0700 Subject: [PATCH 27/28] simplify the condition Co-authored-by: Stephan T. Lavavej Co-authored-by: Matt Stephanson <68978048+MattStephanson@users.noreply.github.com> --- stl/inc/cmath | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 33e4783fda1..96ca06582b2 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -23,8 +23,6 @@ #endif // _HAS_CMATH_INTRINSICS #if _HAS_CXX20 -#include -#include #include #endif // _HAS_CXX20 @@ -1337,18 +1335,6 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { } #if _HAS_CXX20 -template -inline constexpr _Ty _Floating_max; - -template <> -inline constexpr float _Floating_max = FLT_MAX; - -template <> -inline constexpr double _Floating_max = DBL_MAX; - -template <> -inline constexpr long double _Floating_max = LDBL_MAX; - template _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) { if (_STD is_constant_evaluated()) { @@ -1361,7 +1347,10 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - if (_Abs_smaller > 1 && _Abs_larger > _Floating_max> / _Abs_smaller) { + // _Larger is too large to be subnormal, so scaling by 0.5 is exact, and the product _Smaller * _Larger is large + // enough that if _ArgA is subnormal, it will be too small to contribute anyway and this way can sometimes avoid + // overflow problems + if (_Abs_smaller > 1) { return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger; From 77adb6c61d57e1199ef9a372521e28e8c57854eb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 17 Jun 2022 16:08:31 -0700 Subject: [PATCH 28/28] Move comment within the branch it describes. --- stl/inc/cmath | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/cmath b/stl/inc/cmath index 96ca06582b2..21d8fe1d994 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -1347,10 +1347,10 @@ _NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, cons _STD swap(_Abs_smaller, _Abs_larger); } - // _Larger is too large to be subnormal, so scaling by 0.5 is exact, and the product _Smaller * _Larger is large - // enough that if _ArgA is subnormal, it will be too small to contribute anyway and this way can sometimes avoid - // overflow problems if (_Abs_smaller > 1) { + // _Larger is too large to be subnormal, so scaling by 0.5 is exact, and the product _Smaller * _Larger is + // large enough that if _ArgA is subnormal, it will be too small to contribute anyway and this way can + // sometimes avoid overflow problems. return 2 * (_Ty{0.5} * _ArgA + _Smaller * (_Ty{0.5} * _Larger)); } else { return _ArgA + _Smaller * _Larger;