...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Handling of errors by this library is split into two orthogonal parts:
The kinds of errors that can be raised are:
Occurs when one or more arguments to a function are out of range.
Occurs when the particular arguments cause the function to be evaluated at a pole with no well defined residual value. For example if tgamma is evaluated at exactly -2, the function approaches different limiting values depending upon whether you approach from just above or just below -2. Hence the function has no well defined value at this point and a Pole Error will be raised.
Occurs when the result is either infinite, or too large to represent in the numeric type being returned by the function.
Occurs when the result is not zero, but is too small to be represented by any other value in the type being returned by the function.
Occurs when the returned result would be a denormalised value.
Occurs when the argument to one of the rounding functions trunc, round and modf can not be represented as an integer type, is outide the range of the result type.
Occurs when an internal error occured that prevented the result from being evaluated: this should never occur, but if it does, then it's likely to be due to an iterative method not converging fast enough.
Occurs when the result of a function is not defined for the values that were passed to it.
The action undertaken by each error condition is determined by the current Policy in effect. This can be changed program-wide by setting some configuration macros, or at namespace scope, or at the call site (by specifying a specific policy in the function call).
The available actions are:
Throws the exception most appropriate to the error condition.
Sets ::errno to an appropriate value, and then returns the most appropriate result
Ignores the error and simply the returns the most appropriate result.
Calls a user-supplied error handler.
The following tables show all the permutations of errors and actions, with the default action for each error shown in bold:
Table 1. Possible Actions for Domain Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns |
user_error |
Returns the result of |
Table 2. Possible Actions for Pole Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns |
user_error |
Returns the result of |
Table 3. Possible Actions for Overflow Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns |
user_error |
Returns the result of |
Table 4. Possible Actions for Underflow Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns 0 |
user_error |
Returns the result of |
Table 5. Possible Actions for Denorm Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns the denormalised value. |
user_error |
Returns the result of |
Table 6. Possible Actions for Rounding Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns the an unspecified value. |
user_error |
Returns the result of |
Table 7. Possible Actions for Internal Evaluation Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns the closest approximation found. |
user_error |
Returns the result of |
Table 8. Possible Actions for Indeterminate Result Errors
Action |
Behaviour |
---|---|
throw_on_error |
Throws |
errno_on_error |
Sets |
ignore_error |
Returns a default result that depends on the function where the error occurred. |
user_error |
Returns the result of |
The flexibility of the current implementation should be reasonably obvious, the default behaviours were chosen based on feedback during the formal review of this library. It was felt that:
There are some pre-processor macro defines that can be used to change the policy defaults. See also the policy section.
An example is at the Policy tutorial in Changing the Policy Defaults.
Full source code of this typical example of passing a 'bad' argument (negative degrees of freedom) to Student's t distribution is in the error handling example.
The various kind of errors are described in more detail below.
When a special function is passed an argument that is outside the range of values for which that function is defined, then the function returns the result of:
boost::math::policies::raise_domain_error<T>(FunctionName, Message, Val, Policy);
Where T
is the floating-point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, Val is the value that was out
of range, and Policy is the current
policy in use for the function that was called.
The default policy behaviour of this function is to throw a std::domain_error C++ exception. But if the Policy is to ignore the error, or set global ::errno, then a NaN will be returned.
This behaviour is chosen to assist compatibility with the behaviour of ISO/IEC 9899:1999 Programming languages - C and with the Draft Technical Report on C++ Library Extensions, 2005-06-24, section 5.2.1, paragraph 6:
"Each of the functions declared above shall return a NaN (Not a Number) if any argument value is a NaN, but it shall not report a domain error. Otherwise, each of the functions declared above shall report a domain error for just those argument values for which:
"the function description's Returns clause explicitly specifies a domain, and those arguments fall outside the specified domain; or
"the corresponding mathematical function value has a non-zero imaginary component; or
"the corresponding mathematical function is not mathematically defined.
"Note 2: A mathematical function is mathematically defined for a given set of argument values if it is explicitly defined for that set of argument values or if its limiting value exists and does not depend on the direction of approach."
Note that in order to support information-rich error messages when throwing
exceptions, Message
must
contain a Boost.Format
recognised format specifier: the argument Val
is inserted into the error message according to the specifier used.
For example if Message
contains
a "%1%" then it is replaced by the value of Val
to the full precision of T, where as "%.3g" would contain the value
of Val
to 3 digits. See the
Boost.Format documentation
for more details.
When a special function is passed an argument that is at a pole without a well defined residual value, then the function returns the result of:
boost::math::policies::raise_pole_error<T>(FunctionName, Message, Val, Policy);
Where T
is the floating point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, Val
is the value of the argument that is at a pole, and Policy
is the current policy in use for the function that was called.
The default behaviour of this function is to throw a std::domain_error exception.
But error
handling policies can be used to change this, for example to ignore_error
and return NaN.
Note that in order to support information-rich error messages when throwing
exceptions, Message
must
contain a Boost.Format
recognised format specifier: the argument val
is inserted into the error message according to the specifier used.
For example if Message
contains
a "%1%" then it is replaced by the value of val
to the full precision of T, where as "%.3g" would contain the value
of val
to 3 digits. See the
Boost.Format documentation
for more details.
When the result of a special function is too large to fit in the argument floating-point type, then the function returns the result of:
boost::math::policies::raise_overflow_error<T>(FunctionName, Message, Policy);
Where T
is the floating-point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, and Policy
is the current policy in use for the function that was called.
The default policy for this function is that std::overflow_error
C++ exception is thrown. But if, for example, an ignore_error
policy is used, then returns std::numeric_limits<T>::infinity()
.
In this situation if the type T
doesn't support infinities, the maximum value for the type is returned.
If the result of a special function is known to be non-zero, but the calculated result underflows to zero, then the function returns the result of:
boost::math::policies::raise_underflow_error<T>(FunctionName, Message, Policy);
Where T
is the floating point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, and Policy
is the current policy in use for the called function.
The default version of this function returns zero. But with another policy,
like throw_on_error
, throws
an std::underflow_error
C++ exception.
If the result of a special function is a denormalised value z then the function returns the result of:
boost::math::policies::raise_denorm_error<T>(z, FunctionName, Message, Policy);
Where T
is the floating point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, and Policy
is the current policy in use for the called function.
The default version of this function returns z. But
with another policy, like throw_on_error
throws an std::underflow_error
C++ exception.
When a special function calculates a result that is known to be erroneous, or where the result is incalculable then it calls:
boost::math::policies::raise_evaluation_error<T>(FunctionName, Message, Val, Policy);
Where T
is the floating point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, Val
is the erroneous value, and Policy
is the current policy in use for the called function.
The default behaviour of this function is to throw a boost::math::evaluation_error
.
Note that in order to support information rich error messages when throwing
exceptions, Message
must
contain a Boost.Format
recognised format specifier: the argument val
is inserted into the error message according to the specifier used.
For example if Message
contains
a "%1%" then it is replaced by the value of val
to the full precision of T, where as "%.3g" would contain the value
of val
to 3 digits. See the
Boost.Format documentation
for more details.
When the result of a special function is indeterminate for the value that was passed to it, then the function returns the result of:
boost::math::policies::raise_overflow_error<T>(FunctionName, Message, Val, Default, Policy);
Where T
is the floating-point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, Val is the value for which the
result is indeterminate, Default is an alternative default result that must
be returned for ignore_error and errno_on_error policies, and Policy
is the current policy in use for the function that was called.
The default policy for this function is ignore_error
:
note that this error type is reserved for situations where the result is
mathematically undefined or indeterminate, but there is none the less a convention
for what the result should be: for example the C99 standard specifies that
the result of 0^{0} is 1, even though the result is actually mathematically indeterminate.
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 then the value returned is the result of a call to:
boost::math::policies::raise_rounding_error<T>(FunctionName, Message, Val, Policy);
Where T
is the floating point
type passed to the function, FunctionName
is the name of the function, Message
is an error message describing the problem, Val
is the erroneous argument, and Policy
is the current policy in use for the called function.
The default behaviour of this function is to throw a boost::math::rounding_error
.
Note that in order to support information rich error messages when throwing
exceptions, Message
must
contain a Boost.Format
recognised format specifier: the argument val
is inserted into the error message according to the specifier used.
For example if Message
contains
a "%1%" then it is replaced by the value of val
to the full precision of T, where as "%.3g" would contain the value
of val
to 3 digits. See the
Boost.Format documentation
for more details.
Many special functions evaluate their results at a higher precision than their arguments in order to ensure full machine precision in the result: for example, a function passed a float argument may evaluate its result using double precision internally. Many of the errors listed above may therefore occur not during evaluation, but when converting the result to the narrower result type. The function:
template <class T, class Policy, class U> T checked_narrowing_cast(U const& val, const char* function);
Is used to perform these conversions, and will call the error handlers listed above on overflow, underflow or denormalisation.