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

Better error handling #883

Merged
merged 5 commits into from
Nov 26, 2022
Merged
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
2 changes: 2 additions & 0 deletions doc/sf/powers.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ when T1 and T2 are different types.
There are two domains where this is useful: when /y/ is very small, or when
/x/ is close to 1.

Note that for invalid input this function may raise a __domain_error or __overflow_error as appropriate.

Implemented in terms of `expm1`.

The following graph illustrates the behaviour of powm1:
Expand Down
14 changes: 14 additions & 0 deletions include/boost/math/special_functions/beta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,13 @@ T ibeta_imp(T a, T b, T x, const Policy& pol, bool inv, bool normalised, T* p_de

BOOST_MATH_ASSERT((p_derivative == 0) || normalised);

if(!(boost::math::isfinite)(a))
return policies::raise_domain_error<T>(function, "The argument a to the incomplete beta function must be >= zero (got a=%1%).", a, pol);
if(!(boost::math::isfinite)(b))
return policies::raise_domain_error<T>(function, "The argument b to the incomplete beta function must be >= zero (got b=%1%).", b, pol);
if(!(boost::math::isfinite)(x))
return policies::raise_domain_error<T>(function, "The argument x to the incomplete beta function must be in [0,1] (got x=%1%).", x, pol);

if(p_derivative)
*p_derivative = -1; // value not set.

Expand Down Expand Up @@ -1411,6 +1418,13 @@ T ibeta_derivative_imp(T a, T b, T x, const Policy& pol)
//
// start with the usual error checks:
//
if (!(boost::math::isfinite)(a))
return policies::raise_domain_error<T>(function, "The argument a to the incomplete beta function must be >= zero (got a=%1%).", a, pol);
if (!(boost::math::isfinite)(b))
return policies::raise_domain_error<T>(function, "The argument b to the incomplete beta function must be >= zero (got b=%1%).", b, pol);
if (!(boost::math::isfinite)(x))
return policies::raise_domain_error<T>(function, "The argument x to the incomplete beta function must be in [0,1] (got x=%1%).", x, pol);

if(a <= 0)
return policies::raise_domain_error<T>(function, "The argument a to the incomplete beta function must be greater than zero (got a=%1%).", a, pol);
if(b <= 0)
Expand Down
17 changes: 15 additions & 2 deletions include/boost/math/special_functions/detail/ibeta_inverse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,21 @@ T ibeta_inv_imp(T a, T b, T p, T q, const Policy& pol, T* py)
// Try and compute the easy way first:
//
T bet = 0;
if(b < 2)
bet = boost::math::beta(a, b, pol);
if (b < 2)
{
#ifndef BOOST_NO_EXCEPTIONS
try
#endif
{
bet = boost::math::beta(a, b, pol);
}
#ifndef BOOST_NO_EXCEPTIONS
catch (const std::overflow_error&)
{
bet = tools::max_value<T>();
}
#endif
}
if(bet != 0)
{
y = pow(b * q * bet, 1/b);
Expand Down
11 changes: 8 additions & 3 deletions include/boost/math/special_functions/powm1.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/math/special_functions/log1p.hpp>
#include <boost/math/special_functions/expm1.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/math/tools/assert.hpp>

namespace boost{ namespace math{ namespace detail{
Expand All @@ -25,7 +26,6 @@ inline T powm1_imp(const T x, const T y, const Policy& pol)
{
BOOST_MATH_STD_USING
static const char* function = "boost::math::powm1<%1%>(%1%, %1%)";

if (x > 0)
{
if ((fabs(y * (x - 1)) < 0.5) || (fabs(y) < 0.2))
Expand All @@ -40,15 +40,20 @@ inline T powm1_imp(const T x, const T y, const Policy& pol)
// fall through....
}
}
else if (x < 0)
else if ((boost::math::signbit)(x)) // Need to error check -0 here as well
{
// y had better be an integer:
if (boost::math::trunc(y) != y)
return boost::math::policies::raise_domain_error<T>(function, "For non-integral exponent, expected base > 0 but got %1%", x, pol);
if (boost::math::trunc(y / 2) == y / 2)
return powm1_imp(T(-x), y, pol);
}
return pow(x, y) - 1;
T result = pow(x, y) - 1;
if((boost::math::isinf)(result))
return result < 0 ? -boost::math::policies::raise_overflow_error<T>(function, nullptr, pol) : boost::math::policies::raise_overflow_error<T>(function, nullptr, pol);
if((boost::math::isnan)(result))
return boost::math::policies::raise_domain_error<T>(function, "Result of pow is complex or undefined", x, pol);
return result;
}

} // detail
Expand Down
14 changes: 13 additions & 1 deletion include/boost/math/tools/roots.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,19 @@ namespace detail {
last_f0 = f0;
delta2 = delta1;
delta1 = delta;
detail::unpack_tuple(f(result), f0, f1, f2);
#ifndef BOOST_NO_EXCEPTIONS
try
#endif
{
detail::unpack_tuple(f(result), f0, f1, f2);
}
#ifndef BOOST_NO_EXCEPTIONS
catch (const std::overflow_error&)
{
f0 = max > 0 ? tools::max_value<T>() : -tools::min_value<T>();
f1 = f2 = 0;
}
#endif
--count;

BOOST_MATH_INSTRUMENT_VARIABLE(f0);
Expand Down
2 changes: 2 additions & 0 deletions test/git_issue_705.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error

#include "math_unit_test.hpp"
#include <cmath>
#include <boost/math/special_functions/powm1.hpp>
Expand Down
3 changes: 3 additions & 0 deletions test/std_real_concept_check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ struct numeric_limits<boost::math::concepts::std_real_concept>
static const bool traps = false;
static const bool tinyness_before = false;
static const float_round_style round_style = round_toward_zero;
#ifndef BOOST_NO_CXX11_NUMERIC_LIMITS
static const int max_digits10 = digits10 + 2;
#endif
};
}
#endif
Expand Down
18 changes: 18 additions & 0 deletions test/test_ibeta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,24 @@ void test_spots(T)
BOOST_MATH_CHECK_THROW(::boost::math::ibetac(static_cast<T>(2), static_cast<T>(2), static_cast<T>(-0.5)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibetac(static_cast<T>(2), static_cast<T>(2), static_cast<T>(1.5)), std::domain_error);

if (std::numeric_limits<T>::has_quiet_NaN)
{
T n = std::numeric_limits<T>::quiet_NaN();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
}
if (std::numeric_limits<T>::has_infinity)
{
T n = std::numeric_limits<T>::infinity();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(-n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), -n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta(static_cast<T>(2.125), static_cast<T>(1.125), -n), std::domain_error);
}

//
// a = b = 0.5 is a special case:
//
Expand Down
18 changes: 18 additions & 0 deletions test/test_ibeta_derivative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,23 @@ void test_spots(T)
static_cast<T>(4.5),
ldexp(static_cast<T>(1), -557)),
static_cast<T>(5.24647512910420109893867082626308082567071751558842352760e-167L), tolerance * 4);

if (std::numeric_limits<T>::has_quiet_NaN)
{
T n = std::numeric_limits<T>::quiet_NaN();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
}
if (std::numeric_limits<T>::has_infinity)
{
T n = std::numeric_limits<T>::infinity();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(-n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), -n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_derivative(static_cast<T>(2.125), static_cast<T>(1.125), -n), std::domain_error);
}
}

17 changes: 17 additions & 0 deletions test/test_ibeta_inv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,22 @@ void test_spots(T)
static_cast<T>(1) / static_cast<T>(10)),
static_cast<T>(1), tolerance);
}
if (std::numeric_limits<T>::has_quiet_NaN)
{
T n = std::numeric_limits<T>::quiet_NaN();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
}
if (std::numeric_limits<T>::has_infinity)
{
T n = std::numeric_limits<T>::infinity();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(-n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), -n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), static_cast<T>(1.125), -n), std::domain_error);
}
}

24 changes: 24 additions & 0 deletions test/test_ibeta_inv_ab.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,29 @@ void test_beta(T, const char* name)
test_inverses2<T>(ibeta_inva_data, name, "Inverse incomplete beta");
}
#endif
//
// Special spot tests and bug reports:
//
if (std::numeric_limits<T>::has_quiet_NaN)
{
T n = std::numeric_limits<T>::quiet_NaN();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inva(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inva(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inva(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
}
if (std::numeric_limits<T>::has_infinity)
{
T n = std::numeric_limits<T>::infinity();
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), static_cast<T>(1.125), n), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(-n, static_cast<T>(2.125), static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), -n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_invb(static_cast<T>(2.125), static_cast<T>(1.125), -n), std::domain_error);
}

}