diff --git a/include/boost/multiprecision/float128.hpp b/include/boost/multiprecision/float128.hpp index 81d7be741..378e2ced2 100644 --- a/include/boost/multiprecision/float128.hpp +++ b/include/boost/multiprecision/float128.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -710,6 +711,29 @@ inline boost::multiprecision::number rsqr return res; } +// The default std::abs(std::complex<>) implementation normalizes by max(|re|, |im|), +// which yields NaN when an input is infinite (inf/inf). +// Per IEEE 754, the result must be +infinity if either component is infinite, even if the other is NaN. +template +inline boost::multiprecision::number +abs BOOST_PREVENT_MACRO_SUBSTITUTION(const std::complex>& z) +{ + using number_type = boost::multiprecision::number; + const float128_type re_v = z.real().backend().value(); + const float128_type im_v = z.imag().backend().value(); +#ifdef BOOST_MP_USE_FLOAT128 + return number_type(::hypotq(re_v, im_v)); +#else + if (isinfq(re_v) || isinfq(im_v)) + { + return std::numeric_limits::infinity(); + } + const float128_type re_abs = re_v < 0 ? -re_v : re_v; + const float128_type im_abs = im_v < 0 ? -im_v : im_v; + return number_type(sqrtq(re_abs * re_abs + im_abs * im_abs)); +#endif +} + #ifndef BOOST_MP_USE_QUAD template inline boost::multiprecision::number copysign BOOST_PREVENT_MACRO_SUBSTITUTION(const boost::multiprecision::number& a, const boost::multiprecision::number& b) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d261c6f58..ba8eb4c14 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1281,6 +1281,7 @@ test-suite misc : [ run git_issue_636.cpp : : : [ check-target-builds ../config//has_float128 : quadmath : no ] ] [ run git_issue_652.cpp ] [ run git_issue_734.cpp ] + [ run git_issue_759.cpp : : : [ check-target-builds ../config//has_float128 : quadmath : no ] ] [ compile git_issue_98.cpp : [ check-target-builds ../config//has_float128 : TEST_FLOAT128 quadmath : ] [ check-target-builds ../config//has_gmp : TEST_GMP gmp : ] diff --git a/test/git_issue_759.cpp b/test/git_issue_759.cpp new file mode 100644 index 000000000..cd94a5a65 --- /dev/null +++ b/test/git_issue_759.cpp @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2026 Matt Borland. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See: https://github.com/boostorg/multiprecision/issues/759 + +#include +#include +#include +#include +#include "test.hpp" + +int main() +{ + using REAL = boost::multiprecision::float128; + + const REAL inf_v = std::numeric_limits::infinity(); + + { + using std::abs; + const REAL r = abs(inf_v); + BOOST_CHECK((boost::multiprecision::isinf)(r)); + } + + { + using std::abs; + const std::complex z{inf_v, REAL(0)}; + const REAL r = abs(z); + BOOST_CHECK((boost::multiprecision::isinf)(r)); + BOOST_CHECK(!(boost::multiprecision::isnan)(r)); + } + + { + using std::abs; + const std::complex z{REAL(0), inf_v}; + const REAL r = abs(z); + BOOST_CHECK((boost::multiprecision::isinf)(r)); + BOOST_CHECK(!(boost::multiprecision::isnan)(r)); + } + + { + using std::abs; + const std::complex z{-inf_v, REAL(3)}; + const REAL r = abs(z); + BOOST_CHECK((boost::multiprecision::isinf)(r)); + BOOST_CHECK(!(boost::multiprecision::isnan)(r)); + } + + { + // Per IEEE 754: hypot(inf, NaN) = inf. + using std::abs; + const REAL nan_v = std::numeric_limits::quiet_NaN(); + const std::complex z{inf_v, nan_v}; + const REAL r = abs(z); + BOOST_CHECK((boost::multiprecision::isinf)(r)); + } + + { + // Sanity check on a finite value: abs(3 + 4i) == 5. + using std::abs; + const std::complex z{REAL(3), REAL(4)}; + const REAL r = abs(z); + BOOST_CHECK_EQUAL(r, REAL(5)); + } + + return boost::report_errors(); +}