diff --git a/include/ccmath/internal/math/generic/func/power/impl/hypot_impl.hpp b/include/ccmath/internal/math/generic/func/power/impl/hypot_impl.hpp index e2cab091..645c9a96 100644 --- a/include/ccmath/internal/math/generic/func/power/impl/hypot_impl.hpp +++ b/include/ccmath/internal/math/generic/func/power/impl/hypot_impl.hpp @@ -17,6 +17,7 @@ #include "ccmath/math/compare/isnan.hpp" #include "ccmath/math/power/sqrt.hpp" +#include #include namespace ccm::internal::impl @@ -44,6 +45,14 @@ namespace ccm::internal::impl } const T ratio = y / x; - return x * ccm::sqrt(static_cast(1) + ratio * ratio); + + // Compute scale in double precision to avoid float rounding scale to exactly + // 1.0 when ratio is tiny, which would make the overflow guard silently fail. + const double ratio_d = static_cast(ratio); + const double scale_d = ccm::sqrt(1.0 + ratio_d * ratio_d); + + if (static_cast(x) > static_cast(std::numeric_limits::max()) / scale_d) { return fp_bits_t::inf().get_val(); } + + return x * static_cast(scale_d); } } // namespace ccm::internal::impl diff --git a/tests/src/math/power/hypot_cbrt_test.cpp b/tests/src/math/power/hypot_cbrt_test.cpp index 9c5b71cf..7e6c468c 100644 --- a/tests/src/math/power/hypot_cbrt_test.cpp +++ b/tests/src/math/power/hypot_cbrt_test.cpp @@ -103,3 +103,13 @@ TEST(CcmathPowerTests, HypotCbrtCompileTime) static_assert(ccm::hypot(2.0, 3.0, 6.0) == 7.0); static_assert(ccm::cbrt(8.0) == 2.0); } +TEST(CcmathPowerTests, HypotOverflowBoundary) +{ + constexpr float x = 3.40282347e38f; + constexpr float y = 9.18e34f; + + ccm::test::ExpectSameFloatingAsStd( + ccm::hypot(x, y), + std::hypot(x, y) + ); +}