Skip to content

Commit

Permalink
Merge pull request #883 from boostorg/better_error_handling
Browse files Browse the repository at this point in the history
Better error handling
  • Loading branch information
jzmaddock authored Nov 26, 2022
2 parents 23b1fba + ea70672 commit 0dc6a70
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 6 deletions.
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);
}

}

0 comments on commit 0dc6a70

Please sign in to comment.