[libcxx-commits] [libcxx] [libc++][math] Fix undue overflowing of `std::hypot(x, y, z)` (PR #93350)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 22 12:52:14 PDT 2024
https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/93350
>From ced7b5248a3ddec668dea66f29a4cfa647d7ff13 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:12 +0200
Subject: [PATCH 01/22] make use of C++17 is_same_v
---
.../test/std/numerics/c.math/cmath.pass.cpp | 30 +++++++++----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 9379084499792..ad4b46b3713fc 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1136,21 +1136,21 @@ void test_hypot()
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), "");
+ static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (float)0)), float>), "");
+ 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>), "");
assert(std::hypot(2,3,6) == 7);
assert(std::hypot(1,4,8) == 9);
>From f38b1dc4d9d476ef620f004f8669f11caaa3b2b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:13 +0200
Subject: [PATCH 02/22] style change: align argument types
---
.../test/std/numerics/c.math/cmath.pass.cpp | 32 ++++++++++---------
1 file changed, 17 insertions(+), 15 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index ad4b46b3713fc..a16aa0724dffc 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1136,21 +1136,23 @@ void test_hypot()
assert(std::hypot(3,4) == 5);
#if TEST_STD_VER > 14
- static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (float)0)), float>), "");
- 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 off
+ static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (float)0)), float>));
+ 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);
>From 08c57fb775e9e8203c8ef46064c1c5594a168225 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:13 +0200
Subject: [PATCH 03/22] implementation+testing
---
libcxx/include/__math/hypot.h | 78 +++++++++++++++++++
libcxx/include/cmath | 25 +-----
.../test/std/numerics/c.math/cmath.pass.cpp | 45 +++++++++++
3 files changed, 124 insertions(+), 24 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 1bf193a9ab7ee..430640f5e167d 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -9,11 +9,16 @@
#ifndef _LIBCPP___MATH_HYPOT_H
#define _LIBCPP___MATH_HYPOT_H
+#include <__algorithm/max.h>
#include <__config>
+#include <__math/abs.h>
+#include <__math/roots.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_arithmetic.h>
#include <__type_traits/is_same.h>
#include <__type_traits/promote.h>
+#include <array>
+#include <limits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -41,6 +46,79 @@ inline _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2>::type hypot(_A1 __x, _
return __math::hypot((__result_type)__x, (__result_type)__y);
}
+#if _LIBCPP_STD_VER >= 17
+template <class _Real>
+struct __hypot_factors {
+ _Real __threshold;
+ _Real __scale_xyz;
+ _Real __scale_M;
+};
+
+// returns [underflow_factors, overflow_factors]
+template <class _Real>
+std::array<__hypot_factors<_Real>, 2> __create_factors() {
+ static_assert(std::numeric_limits<_Real>::is_iec559);
+
+ __hypot_factors<_Real> __underflow, __overflow;
+ if constexpr (std::is_same_v<_Real, float>) {
+ static_assert(-125 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+128 == std::numeric_limits<_Real>::max_exponent);
+ __underflow = __hypot_factors<_Real>{0x1.0p-62f, 0x1.0p70f, 0x1.0p-70f};
+ __overflow = __hypot_factors<_Real>{0x1.0p62f, 0x1.0p-70f, 0x1.0p+70f};
+ } else if constexpr (std::is_same_v<_Real, double>) {
+ static_assert(-1021 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+1024 == std::numeric_limits<_Real>::max_exponent);
+ __underflow = __hypot_factors<_Real>{0x1.0p-510, 0x1.0p600, 0x1.0p-600};
+ __overflow = __hypot_factors<_Real>{0x1.0p510, 0x1.0p-600, 0x1.0p+600};
+ } else { // long double
+ static_assert(std::is_same_v<_Real, long double>);
+ static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
+ __underflow = __hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
+ __overflow = __hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
+ }
+ return {__underflow, __overflow};
+}
+
+template <class _Real>
+_Real __hypot(_Real __x, _Real __y, _Real __z) {
+ const auto [__underflow, __overflow] = __create_factors<_Real>();
+ _Real __M = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
+ if (__M > __overflow.__threshold) { // x*x + y*y + z*z might overflow
+ __x *= __overflow.__scale_xyz;
+ __y *= __overflow.__scale_xyz;
+ __z *= __overflow.__scale_xyz;
+ __M = __overflow.__scale_M;
+ } else if (__M < __underflow.__threshold) { // x*x + y*y + z*z might underflow
+ __x *= __underflow.__scale_xyz;
+ __y *= __underflow.__scale_xyz;
+ __z *= __underflow.__scale_xyz;
+ __M = __underflow.__scale_M;
+ } else
+ __M = 1;
+ return __M * __math::sqrt(__x * __x + __y * __y + __z * __z);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y, float __z) { return __hypot(__x, __y, __z); }
+
+inline _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y, double __z) { return __hypot(__x, __y, __z); }
+
+inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y, long double __z) {
+ return __hypot(__x, __y, __z);
+}
+
+template <class _A1,
+ class _A2,
+ class _A3,
+ std::enable_if_t< is_arithmetic_v<_A1> && is_arithmetic_v<_A2> && is_arithmetic_v<_A3>, int> = 0 >
+_LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2, _A3>::type hypot(_A1 __x, _A2 __y, _A3 __z) _NOEXCEPT {
+ using __result_type = typename __promote<_A1, _A2, _A3>::type;
+ static_assert(!(
+ std::is_same_v<_A1, __result_type> && std::is_same_v<_A2, __result_type> && std::is_same_v<_A3, __result_type>));
+ return __hypot(static_cast<__result_type>(__x), static_cast<__result_type>(__y), static_cast<__result_type>(__z));
+}
+#endif
+
} // namespace __math
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 7a87e35c84603..e74b50ac86f67 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -305,6 +305,7 @@ constexpr long double lerp(long double a, long double b, long double t) noexcept
*/
#include <__config>
+#include <__math/hypot.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_arithmetic.h>
#include <__type_traits/is_constant_evaluated.h>
@@ -544,30 +545,6 @@ using ::scalbnl _LIBCPP_USING_IF_EXISTS;
using ::tgammal _LIBCPP_USING_IF_EXISTS;
using ::truncl _LIBCPP_USING_IF_EXISTS;
-#if _LIBCPP_STD_VER >= 17
-inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y, float __z) {
- return sqrt(__x * __x + __y * __y + __z * __z);
-}
-inline _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y, double __z) {
- return sqrt(__x * __x + __y * __y + __z * __z);
-}
-inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y, long double __z) {
- return sqrt(__x * __x + __y * __y + __z * __z);
-}
-
-template <class _A1, class _A2, class _A3>
-inline _LIBCPP_HIDE_FROM_ABI
-typename enable_if_t< is_arithmetic<_A1>::value && is_arithmetic<_A2>::value && is_arithmetic<_A3>::value,
- __promote<_A1, _A2, _A3> >::type
-hypot(_A1 __lcpp_x, _A2 __lcpp_y, _A3 __lcpp_z) _NOEXCEPT {
- typedef typename __promote<_A1, _A2, _A3>::type __result_type;
- static_assert(
- !(is_same<_A1, __result_type>::value && is_same<_A2, __result_type>::value && is_same<_A3, __result_type>::value),
- "");
- return std::hypot((__result_type)__lcpp_x, (__result_type)__lcpp_y, (__result_type)__lcpp_z);
-}
-#endif
-
template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool __constexpr_isnan(_A1 __lcpp_x) _NOEXCEPT {
#if __has_builtin(__builtin_isnan)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index a16aa0724dffc..544c197ceb1af 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -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,44 @@ void test_fmin()
assert(std::fmin(1,0) == 0);
}
+struct TestHypot3 {
+ template <class Real>
+ static void operator()() {
+ 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
+ return {1e4000l, 1e3985l};
+ }();
+ 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
+ return {1e-3985l, 1e-4000l};
+ }();
+ check(elem, abs_tol);
+ }
+ }
+};
+
void test_hypot()
{
static_assert((std::is_same<decltype(std::hypot((float)0, (float)0)), float>::value), "");
@@ -1156,6 +1197,10 @@ void test_hypot()
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
}
>From 5d7bc79986e5e0216710e31d5cb2a7438481b275 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:14 +0200
Subject: [PATCH 04/22] handle case: sizeof(double) == sizeof(long double)
---
libcxx/include/__math/hypot.h | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 430640f5e167d..15414cfb7cd1b 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -72,10 +72,14 @@ std::array<__hypot_factors<_Real>, 2> __create_factors() {
__overflow = __hypot_factors<_Real>{0x1.0p510, 0x1.0p-600, 0x1.0p+600};
} else { // long double
static_assert(std::is_same_v<_Real, long double>);
- static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
- static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
- __overflow = __hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
+ if constexpr (sizeof(_Real) == sizeof(double))
+ return static_cast<std::array<__hypot_factors<_Real>, 2>>(__create_factors<double>());
+ else {
+ static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
+ __underflow = __hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
+ __overflow = __hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
+ }
}
return {__underflow, __overflow};
}
>From 5c32b7729946174c77e31c5459379494c75b35fb Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:14 +0200
Subject: [PATCH 05/22] add hide_from_abi macro to internal functions
---
libcxx/include/__math/hypot.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 15414cfb7cd1b..b5a803f3f8ee9 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -56,7 +56,7 @@ struct __hypot_factors {
// returns [underflow_factors, overflow_factors]
template <class _Real>
-std::array<__hypot_factors<_Real>, 2> __create_factors() {
+_LIBCPP_HIDE_FROM_ABI std::array<__hypot_factors<_Real>, 2> __create_factors() {
static_assert(std::numeric_limits<_Real>::is_iec559);
__hypot_factors<_Real> __underflow, __overflow;
@@ -85,7 +85,7 @@ std::array<__hypot_factors<_Real>, 2> __create_factors() {
}
template <class _Real>
-_Real __hypot(_Real __x, _Real __y, _Real __z) {
+_LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
const auto [__underflow, __overflow] = __create_factors<_Real>();
_Real __M = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
if (__M > __overflow.__threshold) { // x*x + y*y + z*z might overflow
>From dfbf54cd5c1c19513375cb99dd072d7576fc6cc4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:14 +0200
Subject: [PATCH 06/22] fully qualify local function calls
---
libcxx/include/__math/hypot.h | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index b5a803f3f8ee9..a0d207c6ca60d 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -56,29 +56,29 @@ struct __hypot_factors {
// returns [underflow_factors, overflow_factors]
template <class _Real>
-_LIBCPP_HIDE_FROM_ABI std::array<__hypot_factors<_Real>, 2> __create_factors() {
+_LIBCPP_HIDE_FROM_ABI std::array<__math::__hypot_factors<_Real>, 2> __create_factors() {
static_assert(std::numeric_limits<_Real>::is_iec559);
- __hypot_factors<_Real> __underflow, __overflow;
+ __math::__hypot_factors<_Real> __underflow, __overflow;
if constexpr (std::is_same_v<_Real, float>) {
static_assert(-125 == std::numeric_limits<_Real>::min_exponent);
static_assert(+128 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __hypot_factors<_Real>{0x1.0p-62f, 0x1.0p70f, 0x1.0p-70f};
- __overflow = __hypot_factors<_Real>{0x1.0p62f, 0x1.0p-70f, 0x1.0p+70f};
+ __underflow = __math::__hypot_factors<_Real>{0x1.0p-62f, 0x1.0p70f, 0x1.0p-70f};
+ __overflow = __math::__hypot_factors<_Real>{0x1.0p62f, 0x1.0p-70f, 0x1.0p+70f};
} else if constexpr (std::is_same_v<_Real, double>) {
static_assert(-1021 == std::numeric_limits<_Real>::min_exponent);
static_assert(+1024 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __hypot_factors<_Real>{0x1.0p-510, 0x1.0p600, 0x1.0p-600};
- __overflow = __hypot_factors<_Real>{0x1.0p510, 0x1.0p-600, 0x1.0p+600};
+ __underflow = __math::__hypot_factors<_Real>{0x1.0p-510, 0x1.0p600, 0x1.0p-600};
+ __overflow = __math::__hypot_factors<_Real>{0x1.0p510, 0x1.0p-600, 0x1.0p+600};
} else { // long double
static_assert(std::is_same_v<_Real, long double>);
if constexpr (sizeof(_Real) == sizeof(double))
- return static_cast<std::array<__hypot_factors<_Real>, 2>>(__create_factors<double>());
+ return static_cast<std::array<__math::__hypot_factors<_Real>, 2>>(__math::__create_factors<double>());
else {
static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
- __overflow = __hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
+ __underflow = __math::__hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
+ __overflow = __math::__hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
}
}
return {__underflow, __overflow};
@@ -86,7 +86,7 @@ _LIBCPP_HIDE_FROM_ABI std::array<__hypot_factors<_Real>, 2> __create_factors() {
template <class _Real>
_LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
- const auto [__underflow, __overflow] = __create_factors<_Real>();
+ const auto [__underflow, __overflow] = __math::__create_factors<_Real>();
_Real __M = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
if (__M > __overflow.__threshold) { // x*x + y*y + z*z might overflow
__x *= __overflow.__scale_xyz;
@@ -103,12 +103,12 @@ _LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
return __M * __math::sqrt(__x * __x + __y * __y + __z * __z);
}
-inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y, float __z) { return __hypot(__x, __y, __z); }
+inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y, float __z) { return __math::__hypot(__x, __y, __z); }
-inline _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y, double __z) { return __hypot(__x, __y, __z); }
+inline _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y, double __z) { return __math::__hypot(__x, __y, __z); }
inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y, long double __z) {
- return __hypot(__x, __y, __z);
+ return __math::__hypot(__x, __y, __z);
}
template <class _A1,
@@ -119,7 +119,7 @@ _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2, _A3>::type hypot(_A1 __x, _A2
using __result_type = typename __promote<_A1, _A2, _A3>::type;
static_assert(!(
std::is_same_v<_A1, __result_type> && std::is_same_v<_A2, __result_type> && std::is_same_v<_A3, __result_type>));
- return __hypot(static_cast<__result_type>(__x), static_cast<__result_type>(__y), static_cast<__result_type>(__z));
+ return __math::__hypot(static_cast<__result_type>(__x), static_cast<__result_type>(__y), static_cast<__result_type>(__z));
}
#endif
>From 912833ea2db23e2fe0cde3c7d87ae0c2f73f1c6b Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:15 +0200
Subject: [PATCH 07/22] remove for operator(): friendlier for older compilers
---
libcxx/test/std/numerics/c.math/cmath.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 544c197ceb1af..6ed37c272192e 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1118,7 +1118,7 @@ void test_fmin()
struct TestHypot3 {
template <class Real>
- static void operator()() {
+ 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));
>From 458b0d3e3d75a2cfefea686ee2d238c3e795dcf2 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:15 +0200
Subject: [PATCH 08/22] document non-naive implementation
---
libcxx/include/__math/hypot.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index a0d207c6ca60d..6636644c77834 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -47,6 +47,7 @@ inline _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2>::type hypot(_A1 __x, _
}
#if _LIBCPP_STD_VER >= 17
+// Factors needed to determine if over-/underflow might happen for `std::hypot(x,y,z)`.
template <class _Real>
struct __hypot_factors {
_Real __threshold;
@@ -54,7 +55,8 @@ struct __hypot_factors {
_Real __scale_M;
};
-// returns [underflow_factors, overflow_factors]
+// Computes `__hypot_factors` needed to determine if over-/underflow might happen for `std::hypot(x,y,z)`.
+// Returns: [underflow_factors, overflow_factors]
template <class _Real>
_LIBCPP_HIDE_FROM_ABI std::array<__math::__hypot_factors<_Real>, 2> __create_factors() {
static_assert(std::numeric_limits<_Real>::is_iec559);
@@ -84,6 +86,10 @@ _LIBCPP_HIDE_FROM_ABI std::array<__math::__hypot_factors<_Real>, 2> __create_fac
return {__underflow, __overflow};
}
+// Computes the three-dimensional hypotenuse: `std::hypot(x,y,z)`.
+// The naive implementation might over-/underflow which is why this implementation is more involved:
+// If the square of an argument might run into issues, we scale the arguments appropriately.
+// See https://github.com/llvm/llvm-project/issues/92782 for a detailed discussion and summary.
template <class _Real>
_LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
const auto [__underflow, __overflow] = __math::__create_factors<_Real>();
>From 9ef79dc60a49e92b10250ebdceec8004020222dd Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:15 +0200
Subject: [PATCH 09/22] remove unnecessary qualifying of std::array in function
return type
---
libcxx/include/__math/hypot.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 6636644c77834..5694e10197617 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -58,7 +58,7 @@ struct __hypot_factors {
// Computes `__hypot_factors` needed to determine if over-/underflow might happen for `std::hypot(x,y,z)`.
// Returns: [underflow_factors, overflow_factors]
template <class _Real>
-_LIBCPP_HIDE_FROM_ABI std::array<__math::__hypot_factors<_Real>, 2> __create_factors() {
+_LIBCPP_HIDE_FROM_ABI array<__math::__hypot_factors<_Real>, 2> __create_factors() {
static_assert(std::numeric_limits<_Real>::is_iec559);
__math::__hypot_factors<_Real> __underflow, __overflow;
>From 36a162ba653a3cbcad7a4fbdb63901f558070ba1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 7 Jul 2024 19:40:16 +0200
Subject: [PATCH 10/22] refactor __hypot_factors, e.g. replace std::array usage
by std::pair
---
libcxx/include/__math/hypot.h | 59 ++++++++++++++---------------------
1 file changed, 24 insertions(+), 35 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 5694e10197617..45608f36eb580 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -17,7 +17,7 @@
#include <__type_traits/is_arithmetic.h>
#include <__type_traits/is_same.h>
#include <__type_traits/promote.h>
-#include <array>
+#include <__utility/pair.h>
#include <limits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -48,42 +48,29 @@ inline _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2>::type hypot(_A1 __x, _
#if _LIBCPP_STD_VER >= 17
// Factors needed to determine if over-/underflow might happen for `std::hypot(x,y,z)`.
+// returns [overflow_threshold, overflow_scale]
template <class _Real>
-struct __hypot_factors {
- _Real __threshold;
- _Real __scale_xyz;
- _Real __scale_M;
-};
-
-// Computes `__hypot_factors` needed to determine if over-/underflow might happen for `std::hypot(x,y,z)`.
-// Returns: [underflow_factors, overflow_factors]
-template <class _Real>
-_LIBCPP_HIDE_FROM_ABI array<__math::__hypot_factors<_Real>, 2> __create_factors() {
+_LIBCPP_HIDE_FROM_ABI std::pair<_Real, _Real> __hypot_factors() {
static_assert(std::numeric_limits<_Real>::is_iec559);
- __math::__hypot_factors<_Real> __underflow, __overflow;
if constexpr (std::is_same_v<_Real, float>) {
static_assert(-125 == std::numeric_limits<_Real>::min_exponent);
static_assert(+128 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __math::__hypot_factors<_Real>{0x1.0p-62f, 0x1.0p70f, 0x1.0p-70f};
- __overflow = __math::__hypot_factors<_Real>{0x1.0p62f, 0x1.0p-70f, 0x1.0p+70f};
+ return {0x1.0p+62f, 0x1.0p-70f};
} else if constexpr (std::is_same_v<_Real, double>) {
static_assert(-1021 == std::numeric_limits<_Real>::min_exponent);
static_assert(+1024 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __math::__hypot_factors<_Real>{0x1.0p-510, 0x1.0p600, 0x1.0p-600};
- __overflow = __math::__hypot_factors<_Real>{0x1.0p510, 0x1.0p-600, 0x1.0p+600};
+ return {0x1.0p+510, 0x1.0p-600};
} else { // long double
static_assert(std::is_same_v<_Real, long double>);
if constexpr (sizeof(_Real) == sizeof(double))
- return static_cast<std::array<__math::__hypot_factors<_Real>, 2>>(__math::__create_factors<double>());
+ return static_cast<std::pair<_Real, _Real>>(__math::__hypot_factors<double>());
else {
static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
- __underflow = __math::__hypot_factors<_Real>{0x1.0p-8'190l, 0x1.0p9'000l, 0x1.0p-9'000l};
- __overflow = __math::__hypot_factors<_Real>{0x1.0p8'190l, 0x1.0p-9'000l, 0x1.0p+9'000l};
+ return {0x1.0p+8'190l, 0x1.0p-9'000l};
}
}
- return {__underflow, __overflow};
}
// Computes the three-dimensional hypotenuse: `std::hypot(x,y,z)`.
@@ -92,21 +79,22 @@ _LIBCPP_HIDE_FROM_ABI array<__math::__hypot_factors<_Real>, 2> __create_factors(
// See https://github.com/llvm/llvm-project/issues/92782 for a detailed discussion and summary.
template <class _Real>
_LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
- const auto [__underflow, __overflow] = __math::__create_factors<_Real>();
- _Real __M = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
- if (__M > __overflow.__threshold) { // x*x + y*y + z*z might overflow
- __x *= __overflow.__scale_xyz;
- __y *= __overflow.__scale_xyz;
- __z *= __overflow.__scale_xyz;
- __M = __overflow.__scale_M;
- } else if (__M < __underflow.__threshold) { // x*x + y*y + z*z might underflow
- __x *= __underflow.__scale_xyz;
- __y *= __underflow.__scale_xyz;
- __z *= __underflow.__scale_xyz;
- __M = __underflow.__scale_M;
+ const _Real __max_abs = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
+ const auto [__overflow_threshold, __overflow_scale] = __math::__hypot_factors<_Real>();
+ _Real __scale;
+ if (__max_abs > __overflow_threshold) { // x*x + y*y + z*z might overflow
+ __scale = __overflow_scale;
+ __x *= __scale;
+ __y *= __scale;
+ __z *= __scale;
+ } else if (__max_abs < 1 / __overflow_threshold) { // x*x + y*y + z*z might underflow
+ __scale = 1 / __overflow_scale;
+ __x *= __scale;
+ __y *= __scale;
+ __z *= __scale;
} else
- __M = 1;
- return __M * __math::sqrt(__x * __x + __y * __y + __z * __z);
+ __scale = 1;
+ return __math::sqrt(__x * __x + __y * __y + __z * __z) / __scale;
}
inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y, float __z) { return __math::__hypot(__x, __y, __z); }
@@ -125,7 +113,8 @@ _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2, _A3>::type hypot(_A1 __x, _A2
using __result_type = typename __promote<_A1, _A2, _A3>::type;
static_assert(!(
std::is_same_v<_A1, __result_type> && std::is_same_v<_A2, __result_type> && std::is_same_v<_A3, __result_type>));
- return __math::__hypot(static_cast<__result_type>(__x), static_cast<__result_type>(__y), static_cast<__result_type>(__z));
+ return __math::__hypot(
+ static_cast<__result_type>(__x), static_cast<__result_type>(__y), static_cast<__result_type>(__z));
}
#endif
>From c276104420491039c46a09a09797a0097176d11f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 16 Jul 2024 19:22:20 +0200
Subject: [PATCH 11/22] fix std::max not available: undef macros
---
libcxx/include/__math/hypot.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 45608f36eb580..60934516e245c 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -24,6 +24,9 @@
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __math {
@@ -121,5 +124,6 @@ _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2, _A3>::type hypot(_A1 __x, _A2
} // namespace __math
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
#endif // _LIBCPP___MATH_HYPOT_H
>From cd4ac8a1695049564e0e932a12c24e4841c2ce8c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 19 Jul 2024 20:37:39 +0200
Subject: [PATCH 12/22] large literal number throwing error: replace if
constexpr by macro guards
---
libcxx/include/__math/hypot.h | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 60934516e245c..195c8621ed448 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -66,13 +66,18 @@ _LIBCPP_HIDE_FROM_ABI std::pair<_Real, _Real> __hypot_factors() {
return {0x1.0p+510, 0x1.0p-600};
} else { // long double
static_assert(std::is_same_v<_Real, long double>);
- if constexpr (sizeof(_Real) == sizeof(double))
- return static_cast<std::pair<_Real, _Real>>(__math::__hypot_factors<double>());
- else {
- static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
- static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
- return {0x1.0p+8'190l, 0x1.0p-9'000l};
- }
+
+ // preprocessor guard necessary, otherwise literals (e.g. `0x1.0p+8'190l`) throw warnings even when shielded by `if
+ // constexpr`
+# if __DBL_MAX_EXP__ == __LDBL_MAX_EXP__
+ static_assert(sizeof(_Real) == sizeof(double));
+ return static_cast<std::pair<_Real, _Real>>(__math::__hypot_factors<double>());
+# else
+ static_assert(sizeof(_Real) > sizeof(double));
+ static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
+ return {0x1.0p+8'190l, 0x1.0p-9'000l};
+# endif
}
}
>From 9b0603725bf1adff2ba3c8436e73fd8e766bd449 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 19 Jul 2024 20:39:46 +0200
Subject: [PATCH 13/22] run clang format
---
.../test/std/numerics/c.math/cmath.pass.cpp | 68 +++++++++----------
1 file changed, 34 insertions(+), 34 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 6ed37c272192e..2fa94d388b87a 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1117,41 +1117,41 @@ void test_fmin()
}
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
- return {1e4000l, 1e3985l};
- }();
- 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
- return {1e-3985l, 1e-4000l};
- }();
- check(elem, abs_tol);
- }
+ 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
+ return {1e4000l, 1e3985l};
+ }();
+ 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
+ return {1e-3985l, 1e-4000l};
+ }();
+ check(elem, abs_tol);
+ }
+ }
};
void test_hypot()
>From 296c520eced0b20ac1cfa56c9b49fca428c0910c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 20:11:03 +0200
Subject: [PATCH 14/22] workaround for std::max(std::initializer_list) not
found
---
libcxx/include/__math/hypot.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 195c8621ed448..bd19f57b5e810 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -87,7 +87,7 @@ _LIBCPP_HIDE_FROM_ABI std::pair<_Real, _Real> __hypot_factors() {
// See https://github.com/llvm/llvm-project/issues/92782 for a detailed discussion and summary.
template <class _Real>
_LIBCPP_HIDE_FROM_ABI _Real __hypot(_Real __x, _Real __y, _Real __z) {
- const _Real __max_abs = std::max({__math::fabs(__x), __math::fabs(__y), __math::fabs(__z)});
+ const _Real __max_abs = std::max(__math::fabs(__x), std::max(__math::fabs(__y), __math::fabs(__z)));
const auto [__overflow_threshold, __overflow_scale] = __math::__hypot_factors<_Real>();
_Real __scale;
if (__max_abs > __overflow_threshold) { // x*x + y*y + z*z might overflow
>From a4dd9911ef5858c3f53b9860756a72ee2f3d8f87 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 20:26:17 +0200
Subject: [PATCH 15/22] make fp_compare.h ready for pre C++11 tests
---
libcxx/test/support/fp_compare.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/support/fp_compare.h b/libcxx/test/support/fp_compare.h
index 1d1933b0bcd81..9bbf64458601a 100644
--- a/libcxx/test/support/fp_compare.h
+++ b/libcxx/test/support/fp_compare.h
@@ -12,13 +12,14 @@
#include <cmath> // for std::abs
#include <algorithm> // for std::max
#include <cassert>
+#include <__config>
// See https://www.boost.org/doc/libs/1_70_0/libs/test/doc/html/boost_test/testing_tools/extended_comparison/floating_point/floating_points_comparison_theory.html
template<typename T>
bool fptest_close(T val, T expected, T eps)
{
- constexpr T zero = T(0);
+ _LIBCPP_CONSTEXPR T zero = T(0);
assert(eps >= zero);
// Handle the zero cases
@@ -33,7 +34,7 @@ bool fptest_close(T val, T expected, T eps)
template<typename T>
bool fptest_close_pct(T val, T expected, T percent)
{
- constexpr T zero = T(0);
+ _LIBCPP_CONSTEXPR T zero = T(0);
assert(percent >= zero);
// Handle the zero cases
>From 7aa67285a84d189ffb28d4d5ae85a1f480df6083 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 20:27:38 +0200
Subject: [PATCH 16/22] fp_compare: DRY remove duplicate test
---
libcxx/test/support/fp_compare.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/libcxx/test/support/fp_compare.h b/libcxx/test/support/fp_compare.h
index 9bbf64458601a..b6f5d7257d4b3 100644
--- a/libcxx/test/support/fp_compare.h
+++ b/libcxx/test/support/fp_compare.h
@@ -37,8 +37,6 @@ bool fptest_close_pct(T val, T expected, T percent)
_LIBCPP_CONSTEXPR T zero = T(0);
assert(percent >= zero);
- // Handle the zero cases
- if (percent == zero) return val == expected;
T eps = (percent / T(100)) * std::max(std::abs(val), std::abs(expected));
return fptest_close(val, expected, eps);
>From 4294e879aee321999845077412dcbe8580152147 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 20:30:51 +0200
Subject: [PATCH 17/22] fp_compare: remove unused variable
---
libcxx/test/support/fp_compare.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/libcxx/test/support/fp_compare.h b/libcxx/test/support/fp_compare.h
index b6f5d7257d4b3..4e344added4b9 100644
--- a/libcxx/test/support/fp_compare.h
+++ b/libcxx/test/support/fp_compare.h
@@ -34,11 +34,8 @@ bool fptest_close(T val, T expected, T eps)
template<typename T>
bool fptest_close_pct(T val, T expected, T percent)
{
- _LIBCPP_CONSTEXPR T zero = T(0);
- assert(percent >= zero);
-
+ assert(percent >= T(0));
T eps = (percent / T(100)) * std::max(std::abs(val), std::abs(expected));
-
return fptest_close(val, expected, eps);
}
>From 266cef6af8f863fdb9a279b9daf05649fc346c8f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 21:03:37 +0200
Subject: [PATCH 18/22] allow transitive includes: cmath (via __hypot) includes
cstddef, cstdint, initializer_list
---
libcxx/include/__math/hypot.h | 9 ++++++---
libcxx/test/libcxx/transitive_includes/cxx17.csv | 3 +++
libcxx/test/libcxx/transitive_includes/cxx20.csv | 3 +++
libcxx/test/libcxx/transitive_includes/cxx23.csv | 3 +++
libcxx/test/libcxx/transitive_includes/cxx26.csv | 3 +++
5 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index bd19f57b5e810..58957968acd5c 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -9,16 +9,19 @@
#ifndef _LIBCPP___MATH_HYPOT_H
#define _LIBCPP___MATH_HYPOT_H
-#include <__algorithm/max.h>
#include <__config>
-#include <__math/abs.h>
-#include <__math/roots.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_arithmetic.h>
#include <__type_traits/is_same.h>
#include <__type_traits/promote.h>
+
+#if _LIBCPP_STD_VER >= 17
+#include <__algorithm/max.h>
+#include <__math/abs.h>
+#include <__math/roots.h>
#include <__utility/pair.h>
#include <limits>
+#endif
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index e03f74f50b914..eb2fd355ee599 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -130,6 +130,9 @@ chrono type_traits
chrono vector
chrono version
cinttypes cstdint
+cmath cstddef
+cmath cstdint
+cmath initializer_list
cmath limits
cmath type_traits
cmath version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 37cd4e58e7fca..298732a234a38 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -135,6 +135,9 @@ chrono type_traits
chrono vector
chrono version
cinttypes cstdint
+cmath cstddef
+cmath cstdint
+cmath initializer_list
cmath limits
cmath type_traits
cmath version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 098752e8699f7..027e3f757b414 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -83,6 +83,9 @@ chrono string_view
chrono vector
chrono version
cinttypes cstdint
+cmath cstddef
+cmath cstdint
+cmath initializer_list
cmath limits
cmath version
codecvt cctype
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 098752e8699f7..027e3f757b414 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -83,6 +83,9 @@ chrono string_view
chrono vector
chrono version
cinttypes cstdint
+cmath cstddef
+cmath cstdint
+cmath initializer_list
cmath limits
cmath version
codecvt cctype
>From 69b3b0787c100f1cf098ddac28e5a53c63f11b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 21:32:38 +0200
Subject: [PATCH 19/22] skip test code for pre C++17
---
libcxx/test/std/numerics/c.math/cmath.pass.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 2fa94d388b87a..7bee010c6efda 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1116,6 +1116,7 @@ void test_fmin()
assert(std::fmin(1,0) == 0);
}
+#if TEST_STD_VER >= 17
struct TestHypot3 {
template <class Real>
void operator()() const {
@@ -1153,6 +1154,7 @@ struct TestHypot3 {
}
}
};
+#endif
void test_hypot()
{
@@ -1176,7 +1178,7 @@ 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
+#if TEST_STD_VER >= 17
// clang-format off
static_assert((std::is_same_v<decltype(std::hypot((float)0, (float)0, (float)0)), float>));
static_assert((std::is_same_v<decltype(std::hypot((float)0, (bool)0, (float)0)), double>));
>From cb9cecce0cdc76ddc9a9136b36326d56c180d9d6 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 20 Jul 2024 22:09:44 +0200
Subject: [PATCH 20/22] clang format
---
libcxx/include/__math/hypot.h | 10 ++++----
libcxx/test/support/fp_compare.h | 39 ++++++++++++++++----------------
2 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 58957968acd5c..e06661faa66d3 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -16,11 +16,11 @@
#include <__type_traits/promote.h>
#if _LIBCPP_STD_VER >= 17
-#include <__algorithm/max.h>
-#include <__math/abs.h>
-#include <__math/roots.h>
-#include <__utility/pair.h>
-#include <limits>
+# include <__algorithm/max.h>
+# include <__math/abs.h>
+# include <__math/roots.h>
+# include <__utility/pair.h>
+# include <limits>
#endif
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/test/support/fp_compare.h b/libcxx/test/support/fp_compare.h
index 4e344added4b9..3088a211dadc3 100644
--- a/libcxx/test/support/fp_compare.h
+++ b/libcxx/test/support/fp_compare.h
@@ -9,35 +9,34 @@
#ifndef SUPPORT_FP_COMPARE_H
#define SUPPORT_FP_COMPARE_H
-#include <cmath> // for std::abs
-#include <algorithm> // for std::max
+#include <cmath> // for std::abs
+#include <algorithm> // for std::max
#include <cassert>
#include <__config>
// See https://www.boost.org/doc/libs/1_70_0/libs/test/doc/html/boost_test/testing_tools/extended_comparison/floating_point/floating_points_comparison_theory.html
-template<typename T>
-bool fptest_close(T val, T expected, T eps)
-{
- _LIBCPP_CONSTEXPR T zero = T(0);
- assert(eps >= zero);
+template <typename T>
+bool fptest_close(T val, T expected, T eps) {
+ _LIBCPP_CONSTEXPR T zero = T(0);
+ assert(eps >= zero);
- // Handle the zero cases
- if (eps == zero) return val == expected;
- if (val == zero) return std::abs(expected) <= eps;
- if (expected == zero) return std::abs(val) <= eps;
+ // Handle the zero cases
+ if (eps == zero)
+ return val == expected;
+ if (val == zero)
+ return std::abs(expected) <= eps;
+ if (expected == zero)
+ return std::abs(val) <= eps;
- return std::abs(val - expected) < eps
- && std::abs(val - expected)/std::abs(val) < eps;
+ return std::abs(val - expected) < eps && std::abs(val - expected) / std::abs(val) < eps;
}
-template<typename T>
-bool fptest_close_pct(T val, T expected, T percent)
-{
- assert(percent >= T(0));
- T eps = (percent / T(100)) * std::max(std::abs(val), std::abs(expected));
- return fptest_close(val, expected, eps);
+template <typename T>
+bool fptest_close_pct(T val, T expected, T percent) {
+ assert(percent >= T(0));
+ T eps = (percent / T(100)) * std::max(std::abs(val), std::abs(expected));
+ return fptest_close(val, expected, eps);
}
-
#endif // SUPPORT_FP_COMPARE_H
>From d13a2c9fe2525e8c9a456e63e7bd27459d6de93c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 21 Jul 2024 20:25:57 +0200
Subject: [PATCH 21/22] remove single quite digit separator (gcc incompatible?)
---
libcxx/include/__math/hypot.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index e06661faa66d3..61fd260c59409 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -77,9 +77,9 @@ _LIBCPP_HIDE_FROM_ABI std::pair<_Real, _Real> __hypot_factors() {
return static_cast<std::pair<_Real, _Real>>(__math::__hypot_factors<double>());
# else
static_assert(sizeof(_Real) > sizeof(double));
- static_assert(-16'381 == std::numeric_limits<_Real>::min_exponent);
- static_assert(+16'384 == std::numeric_limits<_Real>::max_exponent);
- return {0x1.0p+8'190l, 0x1.0p-9'000l};
+ static_assert(-16381 == std::numeric_limits<_Real>::min_exponent);
+ static_assert(+16384 == std::numeric_limits<_Real>::max_exponent);
+ return {0x1.0p+8190l, 0x1.0p-9000l};
# endif
}
}
>From 993ef440157f76ded4e853743f01d98638b20639 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 22 Jul 2024 21:51:30 +0200
Subject: [PATCH 22/22] fix test for case `long double == double`
---
libcxx/test/std/numerics/c.math/cmath.pass.cpp | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/numerics/c.math/cmath.pass.cpp b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
index 7bee010c6efda..19b5fd0cf8996 100644
--- a/libcxx/test/std/numerics/c.math/cmath.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/cmath.pass.cpp
@@ -1135,8 +1135,13 @@ struct TestHypot3 {
return {1e20f, 1e16f};
else if constexpr (std::is_same_v<Real, double>)
return {1e300, 1e287};
- else // long double
- return {1e4000l, 1e3985l};
+ 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);
}
@@ -1147,8 +1152,13 @@ struct TestHypot3 {
return {1e-20f, 1e-24f};
else if constexpr (std::is_same_v<Real, double>)
return {1e-287, 1e-300};
- else // long double
- return {1e-3985l, 1e-4000l};
+ 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);
}
More information about the libcxx-commits
mailing list