Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 3-arg hypot #914

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions include/boost/math/ccmath/hypot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <boost/math/ccmath/abs.hpp>
#include <boost/math/ccmath/isinf.hpp>
#include <boost/math/ccmath/isnan.hpp>
#include <boost/math/ccmath/fmax.hpp>
#include <boost/math/ccmath/detail/swap.hpp>

namespace boost::math::ccmath {
Expand All @@ -44,6 +45,20 @@ constexpr T hypot_impl(T x, T y) noexcept
return x * boost::math::ccmath::sqrt(1 + rat * rat);
}

template <typename T>
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 <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
Expand Down Expand Up @@ -109,6 +124,54 @@ constexpr long double hypotl(long double x, long double y) noexcept
}
#endif

template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, 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<Real>::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 <typename T1, typename T2, typename T3>
constexpr auto hypot(T1 x, T2 y, T3 z) noexcept
{
if (BOOST_MATH_IS_CONSTANT_EVALUATED(x))
{
using promoted_type = tools::promote_args_t<T1, T2, T3>;
return boost::math::ccmath::hypot(static_cast<promoted_type>(x),
static_cast<promoted_type>(y),
static_cast<promoted_type>(z));
}
else
{
using std::hypot;
return hypot(x, y, z);
}
}

} // Namespaces

#endif // BOOST_MATH_CCMATH_HYPOT_HPP
47 changes: 47 additions & 0 deletions test/ccmath_hypot_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,46 @@ constexpr void test()
static_assert(boost::math::ccmath::hypot(T(2), T(2)) == boost::math::ccmath::sqrt(T(8)));
}

template <typename T>
constexpr void test_3_arg()
{
constexpr T tol = 5 * std::numeric_limits<T>::epsilon();

// Error Handling
if constexpr (std::numeric_limits<T>::has_quiet_NaN)
{
static_assert(boost::math::ccmath::isnan(boost::math::ccmath::hypot(std::numeric_limits<T>::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<T>::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<T>::quiet_NaN())), "If z is NaN, NaN is returned");
}

static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(std::numeric_limits<T>::infinity(), T(1), T(1))));
static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(-std::numeric_limits<T>::infinity(), T(1), T(1))));
static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), std::numeric_limits<T>::infinity(), T(1))));
static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), -std::numeric_limits<T>::infinity(), T(1))));
static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), T(1), std::numeric_limits<T>::infinity())));
static_assert(boost::math::ccmath::isinf(boost::math::ccmath::hypot(T(1), T(1), -std::numeric_limits<T>::infinity())));

// Correct promoted types
if constexpr (!std::is_same_v<T, float>)
{
constexpr auto test_type = boost::math::ccmath::hypot(T(1), T(1), 1.0f);
static_assert(std::is_same_v<T, std::remove_cv_t<decltype(test_type)>>);
}
else
{
constexpr auto test_type = boost::math::ccmath::hypot(1.0f, 1, 1);
static_assert(std::is_same_v<double, std::remove_cv_t<decltype(test_type)>>);
}

// 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<float>();
Expand All @@ -67,6 +107,13 @@ int main()
test<boost::multiprecision::float128>();
#endif

test_3_arg<float>();
test_3_arg<double>();

#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
test_3_arg<long double>();
#endif

return 0;
}
#else
Expand Down