[mathpart policy Policies: Controlling Precision, Error Handling etc] [section:pol_overview Policy Overview] [policy_overview] [endsect] [/section:pol_overview Policy Overview] [include policy_tutorial.qbk] [section:pol_ref Policy Reference] [section:error_handling_policies Error Handling Policies] There are two orthogonal aspects to error handling: * What to do (if anything) with the error. * What kind of error is being raised. [h4 Available Actions When an Error is Raised] 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: [h5 throw_on_error] Will throw one of the following exceptions, depending upon the type of the error: [table [[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]] ] [h5 errno_on_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: [table [[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]] ] [h5 ignore_error] Will return one of the values below depending on the error type (`::errno` is NOT changed):: [table [[Error Type][Returned Value]] [[Domain Error][std::numeric_limits::quiet_NaN()]] [[Pole Error][std::numeric_limits::quiet_NaN()]] [[Overflow Error][std::numeric_limits::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]] ] [h5 user_error] 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 T user_domain_error(const char* function, const char* message, const T& val); template T user_pole_error(const char* function, const char* message, const T& val); template T user_overflow_error(const char* function, const char* message, const T& val); template T user_underflow_error(const char* function, const char* message, const T& val); template T user_denorm_error(const char* function, const char* message, const T& val); template T user_rounding_error(const char* function, const char* message, const T& val); template T user_evaluation_error(const char* function, const char* message, const T& val); template 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 [link math_toolkit.pol_tutorial.user_def_err_pol tutorial here]. [h4 Kinds of Error Raised] There are six kinds of error reported by this library, which are summarised in the following table: [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 `boost::math::policies::domain_error` When the action is set to ['throw_on_error] then throws `std::domain_error`]] [[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 `boost::math::policies::pole_error` When the action is ['throw_on_error] then throw a `std::domain_error`]] [[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 `boost::math::policies::overflow_error`. When the action is ['throw_on_error] then throws a `std::overflow_error`.]] [[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 `boost::math::policies::underflow_error` When the specified action is ['throw_on_error] then throws a `std::underflow_error`]] [[Denorm Error] [boost::math::policies::denorm_error<['action]>] [Raised when the result of the function is a denormalised value. Defaults to `boost::math::policies::denorm_error` When the action is ['throw_on_error] then throws a `std::underflow_error`]] [[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 `boost::math::policies::rounding_error` When the action is ['throw_on_error] then throws `boost::math::rounding_error`]] [[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 `boost::math::policies::evaluation_error` When the action is ['throw_on_error] then throws `boost::math::evaluation_error`]] [[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 `boost::math::policies::indeterminate_result_error` When the action is ['throw_on_error] then throws `std::domain_error`]] ] [h4 Examples] 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: [import ../../example/policy_ref_snip1.cpp] [policy_ref_snip1] Suppose we want a statistical distribution to return infinities, rather than throw exceptions, then we can use: [import ../../example/policy_ref_snip2.cpp] [policy_ref_snip2] [endsect] [/section:error_handling_policies Error Handling Policies] [section:internal_promotion Internal Floating-point Promotion Policies] Normally when evaluating a function at say `float` precision, maximal accuracy is assured by conducting the calculation at `double` precision internally, and then rounding the result. There are two policies that control whether internal promotion to a higher precision floating-point type takes place, or not: [table [[Policy][Meaning]] [[`boost::math::policies::promote_float`] [Indicates whether `float` arguments should be promoted to `double` precision internally: defaults to `boost::math::policies::promote_float`]] [[`boost::math::policies::promote_double`] [Indicates whether `double` arguments should be promoted to `long double` precision internally: defaults to `boost::math::policies::promote_double`]] ] [h4 Examples] Suppose we want `tgamma` to be evaluated without internal promotion to `long double`, then we could use: [import ../../example/policy_ref_snip3.cpp] [policy_ref_snip3] Alternatively, suppose we want a distribution to perform calculations without promoting `float` to `double`, then we could use: [import ../../example/policy_ref_snip4.cpp] [policy_ref_snip4] [endsect] [/section:internal_promotion Internal Promotion Policies] [section:assert_undefined Mathematically Undefined Function Policies] There are some functions that are generic (they are present for all the statistical distributions supported) but which may be mathematically undefined for certain distributions, but defined for others. For example, the Cauchy distribution does not have a meaningful mean, so what should mean(cauchy<>()); return, and should such an expression even compile at all? The default behaviour is for all such functions to not compile at all - in fact they will raise a [@http://www.boost.org/libs/static_assert/index.html static assertion] - but by changing the policy we can have them return the result of a domain error instead (which may well throw an exception, depending on the error handling policy). This behaviour is controlled by the `assert_undefined<>` policy: namespace boost{ namespace math{ namespace policies { template class assert_undefined; }}} //namespaces For example: #include using namespace boost::math::policies; using namespace boost::math; // This will not compile, cauchy has no mean! double m1 = mean(cauchy()); // This will compile, but raises a domain error! double m2 = mean(cauchy_distribution > >()); `policy` behaviour can also be obtained by defining the macro #define BOOST_MATH_ASSERT_UNDEFINED_POLICY false at the head of the file - see __policy_macros. [endsect] [/section:assert_undefined Mathematically Undefined Function Policies] [section:discrete_quant_ref Discrete Quantile Policies] If a statistical distribution is ['discrete] then the random variable can only have integer values - this leaves us with a problem when calculating quantiles - we can either ignore the discreteness of the distribution and return a real value, or we can round to an integer. As it happens, computing integer values can be substantially faster than calculating a real value, so there are definite advantages to returning an integer, but we do then need to decide how best to round the result. The `discrete_quantile` policy defines how discrete quantiles work, and how integer results are rounded: enum discrete_quantile_policy_type { real, integer_round_outwards, // default integer_round_inwards, integer_round_down, integer_round_up, integer_round_nearest }; template struct discrete_quantile; The values that `discrete_quantile` can take have the following meanings: [h5 real] Ignores the discreteness of the distribution, and returns a real-valued result. For example: [import ../../example/policy_ref_snip5.cpp] [policy_ref_snip5] Results in `x = 27.3898` and `y = 68.1584`. [h5 integer_round_outwards] This is the default policy: an integer value is returned so that: * Lower quantiles (where the probability is less than 0.5) are rounded down. * Upper quantiles (where the probability is greater than 0.5) are rounded up. This is normally the safest rounding policy, since it ensures that both one and two sided intervals are guaranteed to have ['at least] the requested coverage. For example: [import ../../example/policy_ref_snip6.cpp] [policy_ref_snip6] Results in `x = 27` (rounded down from 27.3898) and `y = 69` (rounded up from 68.1584). The variables x and y are now defined so that: cdf(negative_binomial(20), x) <= 0.05 cdf(negative_binomial(20), y) >= 0.95 In other words we guarantee ['at least 90% coverage in the central region overall], and also ['no more than 5% coverage in each tail]. [h5 integer_round_inwards] This is the opposite of ['integer_round_outwards]: an integer value is returned so that: * Lower quantiles (where the probability is less than 0.5) are rounded ['up]. * Upper quantiles (where the probability is greater than 0.5) are rounded ['down]. For example: [import ../../example/policy_ref_snip7.cpp] [policy_ref_snip7] Results in `x = 28` (rounded up from 27.3898) and `y = 68` (rounded down from 68.1584). The variables x and y are now defined so that: cdf(negative_binomial(20), x) >= 0.05 cdf(negative_binomial(20), y) <= 0.95 In other words we guarantee ['at no more than 90% coverage in the central region overall], and also ['at least 5% coverage in each tail]. [h5 integer_round_down] Always rounds down to an integer value, no matter whether it's an upper or a lower quantile. [h5 integer_round_up] Always rounds up to an integer value, no matter whether it's an upper or a lower quantile. [h5 integer_round_nearest] Always rounds to the nearest integer value, no matter whether it's an upper or a lower quantile. This will produce the requested coverage ['in the average case], but for any specific example may results in either significantly more or less coverage than the requested amount. For example: For example: [import ../../example/policy_ref_snip8.cpp] [policy_ref_snip8] Results in `x = 27` (rounded from 27.3898) and `y = 68` (rounded from 68.1584). [endsect] [/section:discrete_quant_ref Discrete Quantile Policies] [section:precision_pol Precision Policies] There are two equivalent policies that effect the ['working precision] used to calculate results, these policies both default to 0 - meaning calculate to the maximum precision available in the type being used - but can be set to other values to cause lower levels of precision to be used. One might want to trade precision for evaluation speed. namespace boost{ namespace math{ namespace policies{ template digits10; template digits2; }}} // namespaces As you would expect, ['digits10] specifies the number of decimal digits to use, and ['digits2] the number of binary digits. Internally, whichever is used, the precision is always converted to ['binary digits]. These policies are specified at compile-time, because many of the special functions use compile-time-dispatch to select which approximation to use based on the precision requested and the numeric type being used. For example we could calculate `tgamma` to approximately 5 decimal digits using: [import ../../example/policy_ref_snip9.cpp] [policy_ref_snip9] Or again using helper function `make_policy`: [import ../../example/policy_ref_snip10.cpp] [policy_ref_snip10] And for a quantile of a distribution to approximately 25-bit precision: [import ../../example/policy_ref_snip11.cpp] [policy_ref_snip11] [endsect] [/section:precision_pol Precision Policies] [section:iteration_pol Iteration Limits Policies] There are two policies that effect the iterative algorithms used to implement the special functions in this library: template class max_series_iterations; template class max_root_iterations; The class `max_series_iterations` determines the maximum number of iterations permitted in a series evaluation, before the special function gives up and returns the result of __evaluation_error. The class `max_root_iterations` determines the maximum number of iterations permitted in a root-finding algorithm before the special function gives up and returns the result of __evaluation_error. [endsect] [/section:iteration_pol Iteration Limits Policies] [section:policy_defaults Using Macros to Change the Policy Defaults] You can use the various macros below to change any (or all) of the policies. You can make a local change by placing a macro definition *before* a function or distribution #include. [caution There is a danger of One-Definition-Rule violations if you add ad-hoc macros to more than one source files: these must be set the same in *every translation unit*.] [caution If you place it after the #include it will have no effect, (and it will affect only any other following #includes). This is probably not what you intend!] If you want to alter the defaults for any or all of the policies for *all* functions and distributions, installation-wide, then you can do so by defining various macros in [@../../../../boost/math/tools/user.hpp boost/math/tools/user.hpp]. [h5 BOOST_MATH_DOMAIN_ERROR_POLICY] Defines what happens when a domain error occurs, if not defined then defaults to `throw_on_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_POLE_ERROR_POLICY] Defines what happens when a pole error occurs, if not defined then defaults to `throw_on_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_OVERFLOW_ERROR_POLICY] Defines what happens when an overflow error occurs, if not defined then defaults to `throw_on_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_ROUNDING_ERROR_POLICY] Defines what happens when a rounding error occurs, if not defined then defaults to `throw_on_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_EVALUATION_ERROR_POLICY] Defines what happens when an internal evaluation error occurs, if not defined then defaults to `throw_on_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_UNDERFLOW_ERROR_POLICY] Defines what happens when an overflow error occurs, if not defined then defaults to `ignore_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_DENORM_ERROR_POLICY] Defines what happens when a denormalisation error occurs, if not defined then defaults to `ignore_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_INDETERMINATE_RESULT_ERROR_POLICY] Defines what happens when the result is indeterminate, but where there is none the less a convention for the result. If not defined then defaults to `ignore_error`, but can be set to any of the enumerated actions for error handing: `throw_on_error`, `errno_on_error`, `ignore_error` or `user_error`. [h5 BOOST_MATH_DIGITS10_POLICY] Defines how many decimal digits to use in internal computations: defaults to `0` - meaning use all available digits - but can be set to some other decimal value. Since setting this is likely to have a substantial impact on accuracy, it's not generally recommended that you change this from the default. [h5 BOOST_MATH_PROMOTE_FLOAT_POLICY] Determines whether `float` types get promoted to `double` internally to ensure maximum precision in the result, defaults to `true`, but can be set to `false` to turn promotion of `float`'s off. [h5 BOOST_MATH_PROMOTE_DOUBLE_POLICY] Determines whether `double` types get promoted to `long double` internally to ensure maximum precision in the result, defaults to `true`, but can be set to `false` to turn promotion of `double`'s off. [h5 BOOST_MATH_DISCRETE_QUANTILE_POLICY] Determines how discrete quantiles return their results: either as an integer, or as a real value, can be set to one of the enumerated values: `real`, `integer_round_outwards`, `integer_round_inwards`, `integer_round_down`, `integer_round_up`, `integer_round_nearest`. Defaults to `integer_round_outwards`. [h5 BOOST_MATH_ASSERT_UNDEFINED_POLICY] Determines whether functions that are mathematically undefined for a specific distribution compile or raise a static (i.e. compile-time) assertion. Defaults to `true`: meaning that any mathematically undefined function will not compile. When set to `false` then the function will compile but return the result of a domain error: this can be useful for some generic code, that needs to work with all distributions and determine at runtime whether or not a particular property is well defined. [h5 BOOST_MATH_MAX_SERIES_ITERATION_POLICY] Determines how many series iterations a special function is permitted to perform before it gives up and returns an __evaluation_error: Defaults to 1000000. [h5 BOOST_MATH_MAX_ROOT_ITERATION_POLICY] Determines how many root-finding iterations a special function is permitted to perform before it gives up and returns an __evaluation_error: Defaults to 200. [h5 Example] Suppose we want overflow errors to set `::errno` and return an infinity, discrete quantiles to return a real-valued result (rather than round to integer), and for mathematically undefined functions to compile, but return a domain error. Then we could add the following to boost/math/tools/user.hpp: #define BOOST_MATH_OVERFLOW_ERROR_POLICY errno_on_error #define BOOST_MATH_DISCRETE_QUANTILE_POLICY real #define BOOST_MATH_ASSERT_UNDEFINED_POLICY false or we could place these definitions *before* #include using boost::math::normal_distribution; in a source .cpp file. [endsect] [/section:policy_defaults Changing the Policy Defaults] [section:namespace_pol Setting Polices at Namespace Scope] Sometimes what you really want to do is bring all the special functions, or all the distributions into a specific namespace-scope, along with a specific policy to use with them. There are two macros defined to assist with that: BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(Policy) and: BOOST_MATH_DECLARE_DISTRIBUTIONS(Type, Policy) You can use either of these macros after including any special function or distribution header. For example: [import ../../example/policy_ref_snip12.cpp] [policy_ref_snip12] In this example, using BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS results in a set of thin inline forwarding functions being defined: template inline T tgamma(T a){ return ::boost::math::tgamma(a, mypolicy()); } template inline T lgamma(T a) ( return ::boost::math::lgamma(a, mypolicy()); } and so on. Note that while a forwarding function is defined for all the special functions, however, unless you include the specific header for the special function you use (or boost/math/special_functions.hpp to include everything), you will get linker errors from functions that are forward declared, but not defined. We can do the same thing with the distributions, but this time we need to specify the floating-point type to use: [import ../../example/policy_ref_snip13.cpp] [policy_ref_snip13] In this example the result of BOOST_MATH_DECLARE_DISTRIBUTIONS is to declare a typedef for each distribution like this: typedef boost::math::cauchy_distribution cauchy; tyepdef boost::math::gamma_distribution gamma; and so on. The name given to each typedef is the name of the distribution with the "_distribution" suffix removed. [endsect] [/section Changing the Policy Defaults] [section:pol_ref_ref Policy Class Reference] There's very little to say here, the `policy` class is just a rag-bag compile-time container for a collection of policies: ```#include ``` namespace boost{ namespace math{ namespace policies template struct policy { public: typedef ``['computed-from-template-arguments]`` domain_error_type; typedef ``['computed-from-template-arguments]`` pole_error_type; typedef ``['computed-from-template-arguments]`` overflow_error_type; typedef ``['computed-from-template-arguments]`` underflow_error_type; typedef ``['computed-from-template-arguments]`` denorm_error_type; typedef ``['computed-from-template-arguments]`` rounding_error_type; typedef ``['computed-from-template-arguments]`` evaluation_error_type; typedef ``['computed-from-template-arguments]`` indeterminate_result_error_type; typedef ``['computed-from-template-arguments]`` precision_type; typedef ``['computed-from-template-arguments]`` promote_float_type; typedef ``['computed-from-template-arguments]`` promote_double_type; typedef ``['computed-from-template-arguments]`` discrete_quantile_type; typedef ``['computed-from-template-arguments]`` assert_undefined_type; }; template <...argument list...> typename normalise, A1>::type make_policy(...argument list..); template struct normalise { typedef ``computed-from-template-arguments`` type; }; The member typedefs of class `policy` are intended for internal use but are documented briefly here for the sake of completeness. policy<...>::domain_error_type Specifies how domain errors are handled, will be an instance of `boost::math::policies::domain_error<>` with the template argument to `domain_error` one of the `error_policy_type` enumerated values. policy<...>::pole_error_type Specifies how pole-errors are handled, will be an instance of `boost::math::policies::pole_error<>` with the template argument to `pole_error` one of the `error_policy_type` enumerated values. policy<...>::overflow_error_type Specifies how overflow errors are handled, will be an instance of `boost::math::policies::overflow_error<>` with the template argument to `overflow_error` one of the `error_policy_type` enumerated values. policy<...>::underflow_error_type Specifies how underflow errors are handled, will be an instance of `boost::math::policies::underflow_error<>` with the template argument to `underflow_error` one of the `error_policy_type` enumerated values. policy<...>::denorm_error_type Specifies how denorm errors are handled, will be an instance of `boost::math::policies::denorm_error<>` with the template argument to `denorm_error` one of the `error_policy_type` enumerated values. policy<...>::rounding_error_type Specifies how rounding errors are handled, will be an instance of `boost::math::policies::rounding_error<>` with the template argument to `rounding_error` one of the `error_policy_type` enumerated values. policy<...>::evaluation_error_type Specifies how evaluation errors are handled, will be an instance of `boost::math::policies::evaluation_error<>` with the template argument to `evaluation_error` one of the `error_policy_type` enumerated values. policy<...>::indeterminate_error_type Specifies how indeterminate result errors are handled, will be an instance of `boost::math::policies::indeterminate_result_error<>` with the template argument to `indeterminate_result_error` one of the `error_policy_type` enumerated values. policy<...>::precision_type Specifies the internal precision to use in binary digits (uses zero to represent whatever the default precision is). Will be an instance of `boost::math::policies::digits2` which in turn inherits from `boost::boost::integral_constant`. policy<...>::promote_float_type Specifies whether or not to promote `float` arguments to `double` precision internally. Will be an instance of `boost::math::policies::promote_float` which in turn inherits from `boost::integral_constant`. policy<...>::promote_double_type Specifies whether or not to promote `double` arguments to `long double` precision internally. Will be an instance of `boost::math::policies::promote_float` which in turn inherits from `boost::integral_constant`. policy<...>::discrete_quantile_type Specifies how discrete quantiles are evaluated, will be an instance of `boost::math::policies::discrete_quantile<>` instantiated with one of the `discrete_quantile_policy_type` enumerated type. policy<...>::assert_undefined_type Specifies whether mathematically-undefined properties are asserted as compile-time errors, or treated as runtime errors instead. Will be an instance of `boost::math::policies::assert_undefined` which in turn inherits from `boost::integral_constant`. template <...argument list...> typename normalise, A1>::type make_policy(...argument list..); `make_policy` is a helper function that converts a list of policies into a normalised `policy` class. template struct normalise { typedef ``computed-from-template-arguments`` type; }; The `normalise` class template converts one instantiation of the `policy` class into a normalised form. This is used internally to reduce code bloat: so that instantiating a special function on `policy` or `policy` actually both generate the same code internally. Further more, `normalise` can be used to combine a policy with one or more policies: for example many of the special functions will use this to set policies which they don't make use of to their default values, before forwarding to the actual implementation. In this way code bloat is reduced, since the actual implementation depends only on the policy types that they actually use. [endsect] [/section:pol_ref_ref Policy Class Reference] [endsect] [/section:pol_ref Policy Reference] [endmathpart] [/section:policy Policies] [/ policy.qbk Copyright 2007, 2010 John Maddock and Paul A. Bristow. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). ]