Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f762d9d
Recalculate lerp if we got infinity. Eliminates some overflows.
fsb4000 May 11, 2021
96ef790
rename test method and change INFINITY to numeric_limits<_Ty>::infini…
fsb4000 May 11, 2021
337fc81
fix more tests
fsb4000 May 11, 2021
4cb06ef
remove unneeded _STD
fsb4000 May 11, 2021
1d4c290
stl already has constexpr isinf
fsb4000 May 11, 2021
bbfb060
Clear FE_OVERFLOW exception flag
fsb4000 May 12, 2021
13c578f
include <cfenv> instead of <fenv.h>
fsb4000 May 12, 2021
3e7ab76
Check different rounding modes
fsb4000 May 12, 2021
7384454
add some comments
fsb4000 May 12, 2021
18fd0ed
Exception flags could have already been set before calling lerp
fsb4000 May 12, 2021
c06a9d3
use default ctor and dtor
fsb4000 May 12, 2021
c5404ee
use existing code for constexpr signbit
fsb4000 May 13, 2021
d82177b
use _Pretty variable names
fsb4000 May 13, 2021
6bc971e
use fma
fsb4000 May 13, 2021
c202089
use named functions instead of lambdas to reduce duplication between …
fsb4000 Jul 4, 2021
66c6818
Move logic into _Linear_for_lerp. Don't overflow in constant expression.
fsb4000 Jul 4, 2021
6e18a9d
fix collision with macro 'max'
fsb4000 Jul 4, 2021
30b4a68
add missing `#include <limits>`
fsb4000 Jul 4, 2021
672b380
we don't use _Signbit anymore
fsb4000 Jul 4, 2021
e71e883
we don't use <type_traits> anymore
fsb4000 Jul 4, 2021
b2b1b2b
use `<climits>` instead of `<limits>`
fsb4000 Jul 5, 2021
43584bf
use remove_cv_t and actual header is `<cfloat>`
fsb4000 Jul 5, 2021
b288a58
use variable templates
fsb4000 Jul 5, 2021
b541901
Merge branch 'microsoft:main' into fix1917
fsb4000 Jul 22, 2021
7a9ad12
Merge branch 'main' into fix1917
fsb4000 Aug 17, 2021
5b1f373
Merge branch 'main' into fix1917
fsb4000 Sep 26, 2021
76d0fe0
Merge branch 'microsoft:main' into fix1917
fsb4000 Nov 21, 2021
8ded82a
Merge branch 'main' into fix1917
fsb4000 Dec 18, 2021
be88a0d
add short description for test
fsb4000 Dec 18, 2021
aa3cb96
Merge branch 'microsoft:main' into fix1917
fsb4000 Jan 26, 2022
19bd20e
Merge branch 'main' into fix1917 and fix conflicts
fsb4000 May 7, 2022
838cf8f
change test name
fsb4000 May 7, 2022
9f2d248
add a test for `float` and improve the estimate of the inequality
fsb4000 Jun 15, 2022
26ed8e2
Merge branch 'main' into fix1917
fsb4000 Jun 15, 2022
f986102
simplify the condition
fsb4000 Jun 17, 2022
77adb6c
Move comment within the branch it describes.
StephanTLavavej Jun 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion stl/inc/cmath
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,31 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) {
}

#if _HAS_CXX20
template <class _Ty>
_NODISCARD constexpr _Ty _Linear_for_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) {
if (_STD is_constant_evaluated()) {
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) {
// _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;
}
}

return _STD fma(_ArgT, _ArgB - _ArgA, _ArgA);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to find that Twitter thread mocking our huge implementation of lerp to let them know we've added another 25 lines. (No change requested.)

template <class _Ty>
_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
Expand All @@ -1353,7 +1378,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 = _ArgA + _ArgT * (_ArgB - _ArgA);
const auto _Candidate = _Linear_for_lerp(_ArgA, _ArgB, _ArgT);
// monotonic near _ArgT == 1:
if ((_ArgT > 1) == (_ArgB > _ArgA)) {
if (_ArgB > _Candidate) {
Expand Down
81 changes: 81 additions & 0 deletions tests/std/tests/P0811R3_midpoint_lerp/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,86 @@ bool test_lerp() {
return true;
}

void test_gh_1917() {
// GH-1917 <cmath>: lerp(1e+308, 5e+307, 4.0) spuriously overflows
using bit_type = unsigned long long;
using float_bit_type = unsigned int;
STATIC_ASSERT(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
{
ExceptGuard except;

assert(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
assert(check_feexcept(0));
}
STATIC_ASSERT(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
{
ExceptGuard except;

assert(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
assert(check_feexcept(0));
}
#ifdef _M_FP_STRICT
{
ExceptGuard except;
RoundGuard round{FE_UPWARD};

assert(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
assert(check_feexcept(0));
}
{
ExceptGuard except;
RoundGuard round{FE_UPWARD};

assert(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
assert(check_feexcept(0));
}
{
ExceptGuard except;
RoundGuard round{FE_DOWNWARD};

assert(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
assert(check_feexcept(0));
}
{
ExceptGuard except;
RoundGuard round{FE_DOWNWARD};

assert(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
assert(check_feexcept(0));
}
{
ExceptGuard except;
RoundGuard round{FE_TOWARDZERO};

assert(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
assert(check_feexcept(0));
}
{
ExceptGuard except;
RoundGuard round{FE_TOWARDZERO};

assert(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
assert(check_feexcept(0));
}
{
ExceptGuard except;
const int r = feraiseexcept(FE_OVERFLOW);

assert(r == 0);
assert(bit_cast<bit_type>(lerp(1e+308, 5e+307, 4.0)) == bit_cast<bit_type>(-1e+308));
assert(check_feexcept(FE_OVERFLOW));
}
{
ExceptGuard except;
const int r = feraiseexcept(FE_OVERFLOW);

assert(r == 0);
assert(bit_cast<float_bit_type>(lerp(2e+38f, 1e+38f, 4.0f)) == bit_cast<float_bit_type>(-2e+38f));
assert(check_feexcept(FE_OVERFLOW));
}
#endif // _M_FP_STRICT
}

constexpr bool test_gh_2112() {
// GH-2112 <cmath>: std::lerp is missing Arithmetic overloads
assert(lerp(0, 0, 0) == 0.0);
Expand Down Expand Up @@ -1108,6 +1188,7 @@ int main() {
test_lerp<double>();
test_lerp<long double>();

test_gh_1917();
test_gh_2112();
STATIC_ASSERT(test_gh_2112());
}