...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
There are two orthogonal aspects to error handling:
What to do with the error is encapsulated by an enumerated type:
namespace boost { namespace math { namespace policies { enum error_policy_type { throw_on_error = 0, // throw an exception. errno_on_error = 1, // set ::errno & return 0, NaN, infinity or best guess. ignore_error = 2, // return 0, NaN, infinity or best guess. user_error = 3 // call a user-defined error handler. }; }}} // namespaces
The various enumerated values have the following meanings:
Will throw one of the following exceptions, depending upon the type of the error:
Error Type |
Exception |
---|---|
Domain Error |
std::domain_error |
Pole Error |
std::domain_error |
Overflow Error |
std::overflow_error |
Underflow Error |
std::underflow_error |
Denorm Error |
std::underflow_error |
Evaluation Error |
boost::math::evaluation_error |
Indeterminate Result Error |
std::domain_error |
Will set global ::errno
::errno
to one of the following values depending upon the error type (often EDOM
= 33 and ERANGE = 34), and then return the same value as if the error had
been ignored:
Error Type |
errno value |
---|---|
Domain Error |
EDOM |
Pole Error |
EDOM |
Overflow Error |
ERANGE |
Underflow Error |
ERANGE |
Denorm Error |
ERANGE |
Evaluation Error |
EDOM |
Indeterminate Result Error |
EDOM |
Will return one of the values below depending on the error type (::errno
is NOT changed)::
Error Type |
Returned Value |
---|---|
Domain Error |
std::numeric_limits<T>::quiet_NaN() |
Pole Error |
std::numeric_limits<T>::quiet_NaN() |
Overflow Error |
std::numeric_limits<T>::infinity() |
Underflow Error |
0 |
Denorm Error |
The denormalised value. |
Evaluation Error |
The best guess (perhaps NaN) as to the result: which may be significantly in error. |
Indeterminate Result Error |
Depends on the function where the error occurred |
Will call a user defined error handler: these are forward declared in boost/math/policies/error_handling.hpp, but the actual definitions must be provided by the user:
namespace boost{ namespace math{ namespace policies{ template <class T> T user_domain_error(const char* function, const char* message, const T& val); template <class T> T user_pole_error(const char* function, const char* message, const T& val); template <class T> T user_overflow_error(const char* function, const char* message, const T& val); template <class T> T user_underflow_error(const char* function, const char* message, const T& val); template <class T> T user_denorm_error(const char* function, const char* message, const T& val); template <class T> T user_rounding_error(const char* function, const char* message, const T& val); template <class T> T user_evaluation_error(const char* function, const char* message, const T& val); template <class T> T user_indeterminate_result_error(const char* function, const char* message, const T& val); }}} // namespaces
Note that the strings function and message may contain "%1%" format specifiers designed to be used in conjunction with Boost.Format. If these strings are to be presented to the program's end-user then the "%1%" format specifier should be replaced with the name of type T in the function string, and if there is a %1% specifier in the message string then it should be replaced with the value of val.
There is more information on user-defined error handlers in the tutorial here.
There are six kinds of error reported by this library, which are summarised in the following table:
Error Type |
Policy Class |
Description |
---|---|---|
Domain Error |
boost::math::policies::domain_error<action> |
Raised when more or more arguments are outside the defined range of the function.
Defaults to
When the action is set to throw_on_error then
throws |
Pole Error |
boost::math::policies::pole_error<action> |
Raised when more or more arguments would cause the function to be evaluated at a pole.
Defaults to
When the action is throw_on_error then throw
a |
Overflow Error |
boost::math::policies::overflow_error<action> |
Raised when the result of the function is outside the representable range of the floating point type used.
Defaults to
When the action is throw_on_error then throws
a |
Underflow Error |
boost::math::policies::underflow_error<action> |
Raised when the result of the function is too small to be represented in the floating point type used.
Defaults to
When the specified action is throw_on_error
then throws a |
Denorm Error |
boost::math::policies::denorm_error<action> |
Raised when the result of the function is a denormalised value.
Defaults to
When the action is throw_on_error then throws
a |
Rounding Error |
boost::math::policies::rounding_error<action> |
Raised When one of the rounding functions round, trunc or modf is called with an argument that has no integer representation, or is too large to be represented in the result type
Defaults to
When the action is throw_on_error then throws
|
Evaluation Error |
boost::math::policies::evaluation_error<action> |
Raised when the result of the function is well defined and finite, but we were unable to compute it. Typically this occurs when an iterative method fails to converge. Of course ideally this error should never be raised: feel free to report it as a bug if it is!
Defaults to
When the action is throw_on_error then throws
|
Indeterminate Result Error |
boost::math::policies::indeterminate_result_error<action> |
Raised when the result of a function is not defined for the values that were passed to it.
Defaults to
When the action is throw_on_error then throws
|
Suppose we want a call to tgamma
to behave in a C-compatible way and set global ::errno
rather than throw an exception, we
can achieve this at the call site using:
#include <boost/math/special_functions/gamma.hpp> using boost::math::tgamma; //using namespace boost::math::policies; may also be convenient. using boost::math::policies::policy; using boost::math::policies::evaluation_error; using boost::math::policies::domain_error; using boost::math::policies::overflow_error; using boost::math::policies::domain_error; using boost::math::policies::pole_error; using boost::math::policies::errno_on_error; // Define a policy: typedef policy< domain_error<errno_on_error>, pole_error<errno_on_error>, overflow_error<errno_on_error>, evaluation_error<errno_on_error> > my_policy; double my_value = 0.; // // Call the function applying my_policy: double t1 = tgamma(my_value, my_policy()); // Alternatively (and equivalently) we could use helpful function // make_policy and define everything at the call site: double t2 = tgamma(my_value, make_policy( domain_error<errno_on_error>(), pole_error<errno_on_error>(), overflow_error<errno_on_error>(), evaluation_error<errno_on_error>() ) );
Suppose we want a statistical distribution to return infinities, rather than throw exceptions, then we can use:
#include <boost/math/distributions/normal.hpp> using boost::math::normal_distribution; using namespace boost::math::policies; // Define a specific policy: typedef policy< overflow_error<ignore_error> > my_policy; // Define the distribution, using my_policy: typedef normal_distribution<double, my_policy> my_norm; // Construct a my_norm distribution, using default mean and standard deviation, // and get a 0.05 or 5% quantile: double q = quantile(my_norm(), 0.05); // = -1.64485