Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add 3-arg hypot
  • Loading branch information
mborland committed Jan 9, 2023
commit 824f03d5f5b4c01fa81e427b2f4f0902b86fb996
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
51 changes: 51 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,17 @@ 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

#ifdef BOOST_HAS_FLOAT128
test_3_arg<boost::multiprecision::float128>();
#endif

return 0;
}
#else
Expand Down