Error Handling

| Home | Libraries | People | FAQ | More

### Error Handling

##### Quick Reference

Handling of errors by this library is split into two orthogonal parts:

- What kind of error has been raised?
- What should be done when the error is raised?

The kinds of errors that can be raised are:

Domain Error

  Occurs when one or more arguments to a function are out of range.

Pole Error

  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.

Overflow Error

  Occurs when the result is either infinite, or too large to represent in the numeric type being returned by the function.

Underflow Error

  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.

Denormalisation Error

  Occurs when the returned result would be a denormalised value.

Rounding Error

  Occurs when the argument to one of the rounding functions trunc, round and modf can not be represented as an integer type, is outside the range of the result type.

Evaluation Error

  Occurs if no method of evaluation is known, or when an internal error occurred 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.

Indeterminate Result Error

  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:

throw_on_error

  Throws the exception most appropriate to the error condition.

errno_on_error

  Sets ::errno to an appropriate value, and then returns the most appropriate result

ignore_error

  Ignores the error and simply the returns the most appropriate result.

user_error

  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 `std::domain_error`
errno_on_error | Sets `::errno` to `EDOM` and returns `std::numeric_limits<T>::quiet_NaN()`
ignore_error | Returns `std::numeric_limits<T>::quiet_NaN()`
user_error | Returns the result of `boost::math::policies::user_domain_error`: this function must be defined by the user.

Table 2. Possible Actions for Pole Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `std::domain_error`
errno_on_error | Sets `::errno` to `EDOM` and returns `std::numeric_limits<T>::quiet_NaN()`
ignore_error | Returns `std::numeric_limits<T>::quiet_NaN()`
user_error | Returns the result of `boost::math::policies::user_pole_error`: this function must be defined by the user.

Table 3. Possible Actions for Overflow Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `std::overflow_error`
errno_on_error | Sets `::errno` to `ERANGE` and returns `std::numeric_limits<T>::infinity()`
ignore_error | Returns `std::numeric_limits<T>::infinity()`
user_error | Returns the result of `boost::math::policies::user_overflow_error`: this function must be defined by the user.

Table 4. Possible Actions for Underflow Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `std::underflow_error`
errno_on_error | Sets `::errno` to `ERANGE` and returns 0.
ignore_error | Returns 0
user_error | Returns the result of `boost::math::policies::user_underflow_error`: this function must be defined by the user.

Table 5. Possible Actions for Denorm Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `std::underflow_error`
errno_on_error | Sets `::errno` to `ERANGE` and returns the denormalised value.
ignore_error | Returns the denormalised value.
user_error | Returns the result of `boost::math::policies::user_denorm_error`: this function must be defined by the user.

Table 6. Possible Actions for Rounding Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `boost::math::rounding_error`
errno_on_error | Sets `::errno` to `ERANGE` and returns the largest representable value of the target integer type (or the most negative value if the argument to the function was less than zero).
ignore_error | Returns the largest representable value of the target integer type (or the most negative value if the argument to the function was less than zero).
user_error | Returns the result of `boost::math::policies::user_rounding_error`: this function must be defined by the user.

Table 7. Possible Actions for Internal Evaluation Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `boost::math::evaluation_error`
errno_on_error | Sets `::errno` to `EDOM` and returns the closest approximation found.
ignore_error | Returns the closest approximation found.
user_error | Returns the result of `boost::math::policies::user_evaluation_error`: this function must be defined by the user.

Table 8. Possible Actions for Indeterminate Result Errors

Action | Behaviour
--- | ---
throw_on_error | Throws `std::domain_error`
errno_on_error | Sets `::errno` to `EDOM` and returns the same value as `ignore_error`.
ignore_error | Returns a default result that depends on the function where the error occurred.
user_error | Returns the result of `boost::math::policies::user_indeterminate_result_error`: this function must be defined by the user.

All these error conditions are in namespace boost::math::policies, made available, for example, a by namespace declaration using `namespace boost::math::policies;` or individual using declarations `using boost::math::policies::overflow_error;`.

##### Rationale

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:

- Genuine errors should be flagged with exceptions rather than following C-compatible behaviour and setting `::errno`.
- Numeric underflow and denormalised results were not considered to be fatal errors in most cases, so it was felt that these should be ignored.

##### Finding More Information

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.

##### Domain Errors

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.

##### Evaluation at a pole

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.

##### Numeric Overflow

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.

##### Numeric Underflow

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.

##### Denormalisation Errors

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.

##### Evaluation Errors

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.

##### Indeterminate Result Errors

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_erro` 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 00 is 1, even though the result is actually mathematically indeterminate.

##### Rounding Errors

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.

##### Errors from typecasts

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.