-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++][math] Fix undue overflowing of std::hypot(x,y,z)
#93350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ced7b52
f38b1dc
08c57fb
5d7bc79
5c32b77
dfbf54c
912833e
458b0d3
9ef79dc
36a162b
c276104
cd4ac8a
9b06037
296c520
a4dd991
7aa6728
4294e87
266cef6
69b3b07
cb9cecc
d13a2c9
993ef44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,14 +12,17 @@ | |||||
|
||||||
// <cmath> | ||||||
|
||||||
#include <array> | ||||||
#include <cmath> | ||||||
#include <limits> | ||||||
#include <type_traits> | ||||||
#include <cassert> | ||||||
|
||||||
#include "fp_compare.h" | ||||||
#include "test_macros.h" | ||||||
#include "hexfloat.h" | ||||||
#include "truncate_fp.h" | ||||||
#include "type_algorithms.h" | ||||||
|
||||||
// convertible to int/float/double/etc | ||||||
template <class T, int N=0> | ||||||
|
@@ -1113,6 +1116,56 @@ void test_fmin() | |||||
assert(std::fmin(1,0) == 0); | ||||||
} | ||||||
|
||||||
#if TEST_STD_VER >= 17 | ||||||
struct TestHypot3 { | ||||||
template <class Real> | ||||||
void operator()() const { | ||||||
const auto check = [](Real elem, Real abs_tol) { | ||||||
assert(std::isfinite(std::hypot(elem, Real(0), Real(0)))); | ||||||
assert(fptest_close(std::hypot(elem, Real(0), Real(0)), elem, abs_tol)); | ||||||
assert(std::isfinite(std::hypot(elem, elem, Real(0)))); | ||||||
assert(fptest_close(std::hypot(elem, elem, Real(0)), std::sqrt(Real(2)) * elem, abs_tol)); | ||||||
assert(std::isfinite(std::hypot(elem, elem, elem))); | ||||||
assert(fptest_close(std::hypot(elem, elem, elem), std::sqrt(Real(3)) * elem, abs_tol)); | ||||||
}; | ||||||
|
||||||
{ // check for overflow | ||||||
const auto [elem, abs_tol] = []() -> std::array<Real, 2> { | ||||||
if constexpr (std::is_same_v<Real, float>) | ||||||
return {1e20f, 1e16f}; | ||||||
else if constexpr (std::is_same_v<Real, double>) | ||||||
return {1e300, 1e287}; | ||||||
else { // long double | ||||||
# if __DBL_MAX_EXP__ == __LDBL_MAX_EXP__ | ||||||
return {1e300l, 1e287l}; // 64-bit | ||||||
# else | ||||||
return {1e4000l, 1e3985l}; // 80- or 128-bit | ||||||
# endif | ||||||
} | ||||||
}(); | ||||||
check(elem, abs_tol); | ||||||
} | ||||||
|
||||||
{ // check for underflow | ||||||
const auto [elem, abs_tol] = []() -> std::array<Real, 2> { | ||||||
if constexpr (std::is_same_v<Real, float>) | ||||||
return {1e-20f, 1e-24f}; | ||||||
else if constexpr (std::is_same_v<Real, double>) | ||||||
return {1e-287, 1e-300}; | ||||||
else { // long double | ||||||
# if __DBL_MAX_EXP__ == __LDBL_MAX_EXP__ | ||||||
return {1e-287l, 1e-300l}; // 64-bit | ||||||
# else | ||||||
return {1e-3985l, 1e-4000l}; // 80- or 128-bit | ||||||
# endif | ||||||
} | ||||||
}(); | ||||||
check(elem, abs_tol); | ||||||
} | ||||||
} | ||||||
}; | ||||||
#endif | ||||||
|
||||||
void test_hypot() | ||||||
{ | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (float)0)), float>::value), ""); | ||||||
|
@@ -1135,25 +1188,31 @@ void test_hypot() | |||||
static_assert((std::is_same<decltype(hypot(Ambiguous(), Ambiguous())), Ambiguous>::value), ""); | ||||||
assert(std::hypot(3,4) == 5); | ||||||
|
||||||
#if TEST_STD_VER > 14 | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (float)0, (float)0)), float>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (bool)0, (float)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (unsigned short)0, (double)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (int)0, (long double)0)), long double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (double)0, (long)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (long double)0, (unsigned long)0)), long double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (int)0, (long long)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (int)0, (unsigned long long)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (double)0, (double)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (long double)0, (long double)0)), long double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (float)0, (double)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (float)0, (long double)0)), long double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((float)0, (double)0, (long double)0)), long double>::value), ""); | ||||||
static_assert((std::is_same<decltype(std::hypot((int)0, (int)0, (int)0)), double>::value), ""); | ||||||
static_assert((std::is_same<decltype(hypot(Ambiguous(), Ambiguous(), Ambiguous())), Ambiguous>::value), ""); | ||||||
#if TEST_STD_VER >= 17 | ||||||
// clang-format off | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (float)0)), float>)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
While we're at it, you don't need double parens. Also applies to the surrounding lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw that as well, but it is like this all throughout this rather large file. I tried to stay consistent to it... What do you think if I create another mr which changes only this style issue all throughout the file later on? (And keeping it as is for this mr?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds totally reasonable! |
||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (bool)0, (float)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (unsigned short)0, (double)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (int)0, (long double)0)), long double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (double)0, (long)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (long double)0, (unsigned long)0)), long double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (int)0, (long long)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (int)0, (unsigned long long)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (double)0, (double)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (long double)0, (long double)0)), long double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (double)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (long double)0)), long double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((float)0, (double)0, (long double)0)), long double>)); | ||||||
static_assert((std::is_same_v<decltype(std::hypot((int)0, (int)0, (int)0)), double>)); | ||||||
static_assert((std::is_same_v<decltype(hypot(Ambiguous(), Ambiguous(), Ambiguous())), Ambiguous>)); | ||||||
// clang-format on | ||||||
|
||||||
assert(std::hypot(2,3,6) == 7); | ||||||
assert(std::hypot(1,4,8) == 9); | ||||||
|
||||||
// Check for undue over-/underflows of intermediate results. | ||||||
// See discussion at https://github.com/llvm/llvm-project/issues/92782. | ||||||
types::for_each(types::floating_point_types(), TestHypot3()); | ||||||
#endif | ||||||
} | ||||||
|
||||||
|
Uh oh!
There was an error while loading. Please reload this page.