From ced7b5248a3ddec668dea66f29a4cfa647d7ff13 Mon Sep 17 00:00:00 2001 From: Paul 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 93790844997923..ad4b46b3713fcc 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::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); + static_assert((std::is_same_v), ""); 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 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 ad4b46b3713fcc..a16aa0724dffcb 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), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); - static_assert((std::is_same_v), ""); + // clang-format off + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + static_assert((std::is_same_v)); + // 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 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 1bf193a9ab7ee9..430640f5e167d8 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 +#include #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 +struct __hypot_factors { + _Real __threshold; + _Real __scale_xyz; + _Real __scale_M; +}; + +// returns [underflow_factors, overflow_factors] +template +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 +_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 && 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 7a87e35c846036..e74b50ac86f67d 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 -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 ::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 a16aa0724dffcb..544c197ceb1afb 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 @@ // +#include #include #include #include #include +#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 @@ -1113,6 +1116,44 @@ void test_fmin() assert(std::fmin(1,0) == 0); } +struct TestHypot3 { + template + 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 { + if constexpr (std::is_same_v) + return {1e20f, 1e16f}; + else if constexpr (std::is_same_v) + return {1e300, 1e287}; + else // long double + return {1e4000l, 1e3985l}; + }(); + check(elem, abs_tol); + } + + { // check for underflow + const auto [elem, abs_tol] = []() -> std::array { + if constexpr (std::is_same_v) + return {1e-20f, 1e-24f}; + else if constexpr (std::is_same_v) + 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::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 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 430640f5e167d8..15414cfb7cd1b9 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, 2>>(__create_factors()); + 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 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 15414cfb7cd1b9..b5a803f3f8ee99 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 -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 -_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 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 b5a803f3f8ee99..a0d207c6ca60d5 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 -_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, 2>>(__create_factors()); + return static_cast, 2>>(__math::__create_factors()); 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 _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 ::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 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 544c197ceb1afb..6ed37c272192ea 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 - 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 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 a0d207c6ca60d5..6636644c778340 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 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 _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 _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 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 6636644c778340..5694e101976179 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 -_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 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 5694e101976179..45608f36eb580b 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 +#include <__utility/pair.h> #include #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 -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 -_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, 2>>(__math::__create_factors()); + return static_cast>(__math::__hypot_factors()); 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 _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 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 45608f36eb580b..60934516e245c9 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 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 60934516e245c9..195c8621ed4489 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>(__math::__hypot_factors()); - 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>(__math::__hypot_factors()); +# 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 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 6ed37c272192ea..2fa94d388b87ab 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 - 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 { - if constexpr (std::is_same_v) - return {1e20f, 1e16f}; - else if constexpr (std::is_same_v) - return {1e300, 1e287}; - else // long double - return {1e4000l, 1e3985l}; - }(); - check(elem, abs_tol); - } - - { // check for underflow - const auto [elem, abs_tol] = []() -> std::array { - if constexpr (std::is_same_v) - return {1e-20f, 1e-24f}; - else if constexpr (std::is_same_v) - return {1e-287, 1e-300}; - else // long double - return {1e-3985l, 1e-4000l}; - }(); - check(elem, abs_tol); - } + template + 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 { + if constexpr (std::is_same_v) + return {1e20f, 1e16f}; + else if constexpr (std::is_same_v) + return {1e300, 1e287}; + else // long double + return {1e4000l, 1e3985l}; + }(); + check(elem, abs_tol); } + + { // check for underflow + const auto [elem, abs_tol] = []() -> std::array { + if constexpr (std::is_same_v) + return {1e-20f, 1e-24f}; + else if constexpr (std::is_same_v) + 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 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 195c8621ed4489..bd19f57b5e810f 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 _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 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 1d1933b0bcd813..9bbf64458601aa 100644 --- a/libcxx/test/support/fp_compare.h +++ b/libcxx/test/support/fp_compare.h @@ -12,13 +12,14 @@ #include // for std::abs #include // for std::max #include +#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 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 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 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 9bbf64458601aa..b6f5d7257d4b34 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 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 b6f5d7257d4b34..4e344added4b9a 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 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 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 bd19f57b5e810f..58957968acd5c8 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 +#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 e03f74f50b9144..eb2fd355ee5995 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 37cd4e58e7fcad..298732a234a389 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 098752e8699f79..027e3f757b414f 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 098752e8699f79..027e3f757b414f 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 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 2fa94d388b87ab..7bee010c6efda6 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 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::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)); static_assert((std::is_same_v)); From cb9cecce0cdc76ddc9a9136b36326d56c180d9d6 Mon Sep 17 00:00:00 2001 From: Paul 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 58957968acd5c8..e06661faa66d31 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 +# include <__algorithm/max.h> +# include <__math/abs.h> +# include <__math/roots.h> +# include <__utility/pair.h> +# include #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 4e344added4b9a..3088a211dadc3b 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 // for std::abs -#include // for std::max +#include // for std::abs +#include // for std::max #include #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 -bool fptest_close(T val, T expected, T eps) -{ - _LIBCPP_CONSTEXPR T zero = T(0); - assert(eps >= zero); +template +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 -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 +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 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 e06661faa66d31..61fd260c594095 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>(__math::__hypot_factors()); # 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 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 7bee010c6efda6..19b5fd0cf89966 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) 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) 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); }