From f3e0cde514e444c2e25a3522d05a6e244fb2f23a Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Fri, 17 May 2024 19:17:04 +0100 Subject: [PATCH 1/2] Correct float_next(+INF) and float_prior(-INF) Fixes https://github.com/boostorg/math/issues/1132 --- include/boost/math/special_functions/next.hpp | 32 ++++++++++++++----- test/test_next.cpp | 11 +++++-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/boost/math/special_functions/next.hpp b/include/boost/math/special_functions/next.hpp index c696b97b5..02a208e4e 100644 --- a/boost/math/special_functions/next.hpp +++ b/boost/math/special_functions/next.hpp @@ -194,10 +194,14 @@ T float_next_imp(const T& val, const std::true_type&, const Policy& pol) int fpclass = (boost::math::fpclassify)(val); - if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE)) + if (fpclass == (int)FP_INFINITE) { - if(val < 0) + if (val < 0) return -tools::max_value(); + return val; // +INF + } + else if (fpclass == (int)FP_NAN) + { return policies::raise_domain_error( function, "Argument must be finite, but got %1%", val, pol); @@ -243,10 +247,14 @@ T float_next_imp(const T& val, const std::false_type&, const Policy& pol) int fpclass = (boost::math::fpclassify)(val); - if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE)) + if (fpclass == (int)FP_INFINITE) { - if(val < 0) + if (val < 0) return -tools::max_value(); + return val; // +INF + } + else if (fpclass == (int)FP_NAN) + { return policies::raise_domain_error( function, "Argument must be finite, but got %1%", val, pol); @@ -328,10 +336,14 @@ T float_prior_imp(const T& val, const std::true_type&, const Policy& pol) int fpclass = (boost::math::fpclassify)(val); - if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE)) + if (fpclass == (int)FP_INFINITE) { - if(val > 0) + if (val > 0) return tools::max_value(); + return val; // -INF + } + else if (fpclass == (int)FP_NAN) + { return policies::raise_domain_error( function, "Argument must be finite, but got %1%", val, pol); @@ -378,10 +390,14 @@ T float_prior_imp(const T& val, const std::false_type&, const Policy& pol) int fpclass = (boost::math::fpclassify)(val); - if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE)) + if (fpclass == (int)FP_INFINITE) { - if(val > 0) + if (val > 0) return tools::max_value(); + return val; // -INF + } + else if (fpclass == (int)FP_NAN) + { return policies::raise_domain_error( function, "Argument must be finite, but got %1%", val, pol); diff --git a/test/test_next.cpp b/test/test_next.cpp index b4f05b437..8bb5f8d99 100644 --- a/libs/math/test/test_next.cpp +++ b/libs/math/test/test_next.cpp @@ -171,12 +171,12 @@ void test_values(const T& val, const char* name) BOOST_CHECK_EQUAL(boost::math::float_advance(val, primes[i]), v1); BOOST_CHECK_EQUAL(boost::math::float_advance(val, -primes[i]), v2); } - if(std::numeric_limits::is_specialized && (std::numeric_limits::has_infinity)) + BOOST_IF_CONSTEXPR(std::numeric_limits::is_specialized && (std::numeric_limits::has_infinity)) { BOOST_CHECK_EQUAL(boost::math::float_prior(std::numeric_limits::infinity()), (std::numeric_limits::max)()); BOOST_CHECK_EQUAL(boost::math::float_next(-std::numeric_limits::infinity()), -(std::numeric_limits::max)()); - BOOST_MATH_CHECK_THROW(boost::math::float_prior(-std::numeric_limits::infinity()), std::domain_error); - BOOST_MATH_CHECK_THROW(boost::math::float_next(std::numeric_limits::infinity()), std::domain_error); + BOOST_CHECK_EQUAL(boost::math::float_prior(-std::numeric_limits::infinity()), -std::numeric_limits::infinity()); + BOOST_CHECK_EQUAL(boost::math::float_next(std::numeric_limits::infinity()), std::numeric_limits::infinity()); if(boost::math::policies:: BOOST_MATH_OVERFLOW_ERROR_POLICY == boost::math::policies::throw_on_error) { BOOST_MATH_CHECK_THROW(boost::math::float_prior(-(std::numeric_limits::max)()), std::overflow_error); @@ -188,6 +188,11 @@ void test_values(const T& val, const char* name) BOOST_CHECK_EQUAL(boost::math::float_next((std::numeric_limits::max)()), std::numeric_limits::infinity()); } } + BOOST_IF_CONSTEXPR(std::numeric_limits::is_specialized && (std::numeric_limits::has_quiet_NaN)) + { + BOOST_MATH_CHECK_THROW(boost::math::float_prior((std::numeric_limits::quiet_NaN)()), std::domain_error); + BOOST_MATH_CHECK_THROW(boost::math::float_next((std::numeric_limits::quiet_NaN)()), std::domain_error); + } // // We need to test float_distance over multiple orders of magnitude, // the only way to get an accurate true result is to count the representations -- 2.45.1