...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 a number of Policies that can be used to trade accuracy for performance:
float
arguments are evaluated at double
precision internally to ensure full precision in the result. Similarly
double
precision functions
are evaluated at long double
precision internally by default. Changing these defaults can have a significant
speed advantage at the expense of accuracy, note also that evaluating using
float
internally may result
in numerical instability for some of the more complex algorithms, we suggest
you use this option with care.
double
precision doesn't mean you necessarily want to target full 16-digit accuracy,
if you wish you can change the default (full machine precision) to whatever
is "good enough" for your particular use case.
For example, suppose you want to evaluate double
precision functions at double
precision internally, you can change the global default by passing -DBOOST_MATH_PROMOTE_DOUBLE_POLICY=false
on the
command line, or at the point of call via something like this:
double val = boost::math::erf(my_argument, boost::math::policies::make_policy(boost::math::policies::promote_double<false>()));
However, an easier option might be:
#include <boost/math/special_functions.hpp> // Or any individual special function header namespace math{ namespace precise{ // // Define a Policy for accurate evaluation - this is the same as the default, unless // someone has changed the global defaults. // typedef boost::math::policies::policy<> accurate_policy; // // Invoke BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS to declare // functions that use the above policy. Note no trailing // ";" required on the macro call: // BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(accurate_policy) } namespace fast{ // // Define a Policy for fast evaluation: // using namespace boost::math::policies[ typedef policy<promote_double<false> > fast_policy; // // Invoke BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS: // BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(fast_policy) } }
And now one can call:
math::accurate::tgamma(x);
For the "accurate" version of tgamma, and:
math::fast::tgamma(x);
For the faster version.
Had we wished to change the target precision (to 9 decimal places) as well as the evaluation type used, we might have done:
namespace math{ namespace fast{ // // Define a Policy for fast evaluation: // using namespace boost::math::policies; typedef policy<promote_double<false>, digits10<9> > fast_policy; // // Invoke BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS: // BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(fast_policy) } }
One can do a similar thing with the distribution classes:
#include <boost/math/distributions.hpp> // or any individual distribution header namespace math{ namespace fast{ // // Define a policy for fastest possible evaluation: // using namespace boost::math::policies; typedef policy<promote_float<false> > fast_float_policy; // // Invoke BOOST_MATH_DECLARE_DISTRIBUTIONS // BOOST_MATH_DECLARE_DISTRIBUTIONS(float, fast_float_policy) }} // namespaces // // And use: // float p_val = cdf(math::fast::normal(1.0f, 3.0f), 0.25f);
Here's how these options change the relative performance of the distributions on Linux:
Table 22.2. Distribution performance comparison for different performance options with GNU C++ version 9.2.1 20191008 on linux
Function |
boost 1.73 |
Boost |
Boost |
Boost |
---|---|---|---|---|
ArcSine (CDF) |
1.75 |
1.65 |
1.65 |
1.00 |
ArcSine (PDF) |
1.00 |
1.00 |
1.00 |
1.20 |
ArcSine (quantile) |
1.04 |
1.00 |
1.04 |
1.00 |
Beta (CDF) |
4.41 |
1.40 |
1.38 |
1.00 |
Beta (PDF) |
4.29 |
1.23 |
1.24 |
1.00 |
Beta (quantile) |
4.17 |
1.60 |
1.44 |
1.00 |
Binomial (CDF) |
3.97 |
1.54 |
1.26 |
1.00 |
Binomial (PDF) |
2.90 |
1.09 |
1.09 |
1.00 |
Binomial (quantile) |
3.88 |
1.53 |
1.26 |
1.00 |
Cauchy (CDF) |
1.15 |
1.15 |
1.00 |
1.00 |
Cauchy (PDF) |
1.00 |
1.00 |
1.00 |
1.33 |
Cauchy (quantile) |
1.88 |
2.00 |
1.79 |
1.00 |
ChiSquared (CDF) |
3.54 |
1.79 |
1.40 |
1.00 |
ChiSquared (PDF) |
4.84 |
1.41 |
1.38 |
1.00 |
ChiSquared (quantile) |
3.76 |
1.84 |
1.37 |
1.00 |
Exponential (CDF) |
1.27 |
1.87 |
1.60 |
1.00 |
Exponential (PDF) |
1.58 |
1.84 |
2.00 |
1.00 |
Exponential (quantile) |
1.00 |
1.13 |
1.17 |
1.04 |
ExtremeValue (CDF) |
1.78 |
1.81 |
1.81 |
1.00 |
ExtremeValue (PDF) |
1.96 |
1.85 |
1.83 |
1.00 |
ExtremeValue (quantile) |
1.43 |
1.38 |
1.43 |
1.00 |
F (CDF) |
4.46 |
1.58 |
1.24 |
1.00 |
F (PDF) |
3.37 |
1.18 |
1.12 |
1.00 |
F (quantile) |
2.70 |
1.28 |
1.03 |
1.00 |
Gamma (CDF) |
3.90 |
1.46 |
1.47 |
1.00 |
Gamma (PDF) |
5.75 |
1.49 |
1.51 |
1.00 |
Gamma (quantile) |
4.35 |
1.80 |
1.22 |
1.00 |
Geometric (CDF) |
1.00 |
1.08 |
1.08 |
1.16 |
Geometric (PDF) |
1.44 |
1.44 |
1.56 |
1.00 |
Geometric (quantile) |
1.00 |
1.12 |
1.04 |
1.24 |
Hypergeometric (CDF) |
1.08 |
1.00 |
1.02 |
1.07 |
Hypergeometric (PDF) |
1.16 |
1.00 |
1.02 |
1.09 |
Hypergeometric (quantile) |
1.00 |
1.27 |
1.24 |
1.14 |
InverseChiSquared (CDF) |
3.06 |
1.60 |
1.31 |
1.00 |
InverseChiSquared (PDF) |
4.42 |
1.63 |
1.52 |
1.00 |
InverseChiSquared (quantile) |
2.92 |
1.66 |
1.21 |
1.00 |
InverseGamma (CDF) |
3.17 |
1.41 |
1.18 |
1.00 |
InverseGamma (PDF) |
5.26 |
1.54 |
1.49 |
1.00 |
InverseGamma (quantile) |
4.02 |
1.69 |
1.19 |
1.00 |
InverseGaussian (CDF) |
1.89 |
1.94 |
1.99 |
1.00 |
InverseGaussian (PDF) |
1.53 |
1.53 |
1.53 |
1.00 |
InverseGaussian (quantile) |
2.09 |
2.26 |
2.14 |
1.00 |
Laplace (CDF) |
1.58 |
1.62 |
1.62 |
1.00 |
Laplace (PDF) |
1.56 |
1.59 |
1.59 |
1.00 |
Laplace (quantile) |
1.39 |
1.43 |
1.36 |
1.00 |
LogNormal (CDF) |
2.12 |
1.33 |
1.36 |
1.00 |
LogNormal (PDF) |
1.68 |
1.73 |
1.70 |
1.00 |
LogNormal (quantile) |
1.75 |
1.68 |
1.40 |
1.00 |
Logistic (CDF) |
1.52 |
1.52 |
1.74 |
1.00 |
Logistic (PDF) |
1.59 |
1.59 |
1.59 |
1.00 |
Logistic (quantile) |
1.23 |
1.23 |
1.27 |
1.00 |
NegativeBinomial (CDF) |
4.51 |
1.80 |
1.37 |
1.00 |
NegativeBinomial (PDF) |
3.62 |
1.07 |
1.07 |
1.00 |
NegativeBinomial (quantile) |
3.26 |
1.27 |
1.00 |
1.15 |
NonCentralBeta (CDF) |
4.88 |
1.80 |
1.61 |
1.00 |
NonCentralBeta (PDF) |
3.83 |
1.55 |
1.42 |
1.00 |
NonCentralBeta (quantile) |
7.51 |
2.59 |
2.26 |
1.00 |
NonCentralChiSquared (CDF) |
2.90 |
1.82 |
1.43 |
1.00 |
NonCentralChiSquared (PDF) |
3.52 |
1.61 |
1.54 |
1.00 |
NonCentralChiSquared (quantile) |
4.15 |
2.33 |
1.56 |
1.00 |
NonCentralF (CDF) |
4.26 |
1.55 |
1.36 |
1.00 |
NonCentralF (PDF) |
3.98 |
1.49 |
1.29 |
1.00 |
NonCentralF (quantile) |
4.52 |
1.57 |
1.31 |
1.00 |
NonCentralT (CDF) |
3.79 |
1.72 |
1.52 |
1.00 |
NonCentralT (PDF) |
3.41 |
1.93 |
1.75 |
1.00 |
NonCentralT (quantile) |
5.06 |
2.17 |
1.65 |
1.00 |
Normal (CDF) |
2.24 |
1.34 |
1.28 |
1.00 |
Normal (PDF) |
1.55 |
1.50 |
1.59 |
1.00 |
Normal (quantile) |
1.68 |
1.18 |
1.24 |
1.00 |
Pareto (CDF) |
1.40 |
1.46 |
1.40 |
1.00 |
Pareto (PDF) |
1.95 |
1.95 |
2.09 |
1.00 |
Pareto (quantile) |
1.76 |
1.79 |
1.72 |
1.00 |
Poisson (CDF) |
3.38 |
1.56 |
1.45 |
1.00 |
Poisson (PDF) |
4.19 |
1.54 |
1.52 |
1.00 |
Poisson (quantile) |
3.04 |
1.38 |
1.35 |
1.00 |
Rayleigh (CDF) |
1.35 |
1.41 |
1.41 |
1.00 |
Rayleigh (PDF) |
1.68 |
1.64 |
1.64 |
1.00 |
Rayleigh (quantile) |
1.04 |
1.04 |
1.00 |
1.08 |
SkewNormal (CDF) |
1.43 |
1.31 |
1.31 |
1.00 |
SkewNormal (PDF) |
2.07 |
1.31 |
1.29 |
1.00 |
SkewNormal (quantile) |
2.16 |
1.85 |
1.31 |
1.00 |
StudentsT (CDF) |
6.44 |
2.23 |
2.21 |
1.00 |
StudentsT (PDF) |
6.58 |
1.60 |
1.52 |
1.00 |
StudentsT (quantile) |
5.79 |
1.87 |
1.86 |
1.00 |
Weibull (CDF) |
1.73 |
1.73 |
1.75 |
1.00 |
Weibull (PDF) |
1.98 |
1.98 |
1.98 |
1.00 |
Weibull (quantile) |
1.21 |
1.21 |
1.19 |
1.00 |