From 824f03d5f5b4c01fa81e427b2f4f0902b86fb996 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Jan 2023 07:38:14 -0800 Subject: [PATCH 1/2] Add 3-arg hypot --- include/boost/math/ccmath/hypot.hpp | 63 +++++++++++++++++++++++++++++ test/ccmath_hypot_test.cpp | 51 +++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/include/boost/math/ccmath/hypot.hpp b/include/boost/math/ccmath/hypot.hpp index 613b2681ac..2a48a80c59 100644 --- a/include/boost/math/ccmath/hypot.hpp +++ b/include/boost/math/ccmath/hypot.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace boost::math::ccmath { @@ -44,6 +45,20 @@ constexpr T hypot_impl(T x, T y) noexcept return x * boost::math::ccmath::sqrt(1 + rat * rat); } +template +constexpr T hypot_impl(T x, T y, T z) noexcept +{ + x = boost::math::ccmath::abs(x); + y = boost::math::ccmath::abs(y); + z = boost::math::ccmath::abs(z); + + T a = boost::math::ccmath::fmax(boost::math::ccmath::fmax(x, y), z); + + return a * boost::math::ccmath::sqrt((x / a) * (x / a) + + (y / a) * (y / a) + + (z / a) * (z / a)); +} + } // Namespace detail template , bool> = true> @@ -109,6 +124,54 @@ constexpr long double hypotl(long double x, long double y) noexcept } #endif +template , bool> = true> +constexpr auto hypot(Real x, Real y, Real z) noexcept +{ + if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) + { + if (boost::math::ccmath::isinf(x) || boost::math::ccmath::isinf(y) || boost::math::ccmath::isinf(z)) + { + return std::numeric_limits::infinity(); + } + else if (boost::math::ccmath::isnan(x)) + { + return x; + } + else if (boost::math::ccmath::isnan(y)) + { + return y; + } + else if (boost::math::ccmath::isnan(z)) + { + return z; + } + + return detail::hypot_impl(x, y, z); + } + else + { + using std::hypot; + return hypot(x, y, z); + } +} + +template +constexpr auto hypot(T1 x, T2 y, T3 z) noexcept +{ + if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) + { + using promoted_type = tools::promote_args_t; + return boost::math::ccmath::hypot(static_cast(x), + static_cast(y), + static_cast(z)); + } + else + { + using std::hypot; + return hypot(x, y, z); + } +} + } // Namespaces #endif // BOOST_MATH_CCMATH_HYPOT_HPP diff --git a/test/ccmath_hypot_test.cpp b/test/ccmath_hypot_test.cpp index bc85b65962..c4cbc32c5d 100644 --- a/test/ccmath_hypot_test.cpp +++ b/test/ccmath_hypot_test.cpp @@ -54,6 +54,46 @@ constexpr void test() static_assert(boost::math::ccmath::hypot(T(2), T(2)) == boost::math::ccmath::sqrt(T(8))); } +template +constexpr void test_3_arg() +{ + constexpr T tol = 5 * std::numeric_limits::epsilon(); + + // Error Handling + if constexpr (std::numeric_limits::has_quiet_NaN) + { + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::hypot(std::numeric_limits::quiet_NaN(), T(1), T(1))), "If x is NaN, NaN is returned"); + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::hypot(T(1), std::numeric_limits::quiet_NaN(), T(1))), "If y is NaN, NaN is returned"); + static_assert(boost::math::ccmath::isnan(boost::math::ccmath::hypot(T(1), T(1), std::numeric_limits::quiet_NaN())), "If z is NaN, NaN is returned"); + } + + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(std::numeric_limits::infinity(), T(1), T(1)))); + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(-std::numeric_limits::infinity(), T(1), T(1)))); + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), std::numeric_limits::infinity(), T(1)))); + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), -std::numeric_limits::infinity(), T(1)))); + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), T(1), std::numeric_limits::infinity()))); + static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), T(1), -std::numeric_limits::infinity()))); + + // Correct promoted types + if constexpr (!std::is_same_v) + { + constexpr auto test_type = boost::math::ccmath::hypot(T(1), T(1), 1.0f); + static_assert(std::is_same_v>); + } + else + { + constexpr auto test_type = boost::math::ccmath::hypot(1.0f, 1, 1); + static_assert(std::is_same_v>); + } + + // Functionality + static_assert(boost::math::ccmath::hypot(T(1), T(1), T(1)) == boost::math::ccmath::sqrt(T(3))); + static_assert(boost::math::ccmath::hypot(T(-1), T(1), T(1)) == boost::math::ccmath::sqrt(T(3))); + static_assert(boost::math::ccmath::hypot(T(2), T(2), T(1)) == T(3)); + static_assert(boost::math::ccmath::hypot(T(2), T(-2), T(1)) == T(3)); + static_assert(boost::math::ccmath::abs(boost::math::ccmath::hypot(T(1), T(2), T(3)) - boost::math::ccmath::sqrt(T(14))) < tol); +} + int main() { test(); @@ -67,6 +107,17 @@ int main() test(); #endif + test_3_arg(); + test_3_arg(); + + #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + test_3_arg(); + #endif + + #ifdef BOOST_HAS_FLOAT128 + test_3_arg(); + #endif + return 0; } #else From b298d71bfa8923a43d80938b69dc4ddd3682d55e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 10 Jan 2023 14:51:55 -0800 Subject: [PATCH 2/2] Remove float128 testing --- test/ccmath_hypot_test.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/ccmath_hypot_test.cpp b/test/ccmath_hypot_test.cpp index c4cbc32c5d..9015ed32a2 100644 --- a/test/ccmath_hypot_test.cpp +++ b/test/ccmath_hypot_test.cpp @@ -113,10 +113,6 @@ int main() #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS test_3_arg(); #endif - - #ifdef BOOST_HAS_FLOAT128 - test_3_arg(); - #endif return 0; }