...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The library provides some helper code to assist in defining new constants;
the process for defining a constant called my_constant
goes like this:
1. Define a function that calculates the value of the
constant. This should be a template function, and be placed in
boost/math/constants/calculate_constants.hpp
if
the constant is to be added to this library, or else defined at the top of
your source file if not.
The function should look like this:
namespace boost{ namespace math{ namespace constants{ namespace detail{ template <class Real> template <int N> Real constant_my_constant<Real>::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_<N>)) { int required_precision = N ? N : tools::digits<Real>(); Real result = /* value computed to required_precision bits */ ; return result; } }}}} // namespaces
Then define a placeholder for the constant itself:
namespace boost{ namespace math{ namespace constants{ BOOST_DEFINE_MATH_CONSTANT(my_constant, 0.0, "0"); }}}
For example, to calculate π/2, add to boost/math/constants/calculate_constants.hpp
template <class T> template<int N> inline T constant_half_pi<T>::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_<N>)) { BOOST_MATH_STD_USING return pi<T, policies::policy<policies::digits2<N> > >() / static_cast<T>(2); }
Then to boost/math/constants/constants.hpp
add:
BOOST_DEFINE_MATH_CONSTANT(half_pi, 0.0, "0"); // Actual values are temporary, we'll replace them later.
Note | |
---|---|
Previously defined constants like pi and e can be used, but by not simply calling |
Warning | |
---|---|
Newly defined constants can only be used once they are included in error C2143: syntax error : missing ';' before '<' error C2433: 'constant_root_two_div_pi' : 'inline' not permitted on data declarations error C2888: 'T constant_root_two_div_pi' : symbol cannot be defined within namespace 'detail' error C2988: unrecognizable template declaration/definition
|
2. You will need an arbitrary precision type to use
to calculate the value. This library currently supports either
cpp_float
, NTL::RR
or mpfr_class
used via the
bindings in boost/math/bindings
.
The default is to use NTL::RR
unless
you define an alternate macro, for example, USE_MPFR
or USE_CPP_FLOAT
at the start
of your program.
3. It is necessary to link to the Boost.Regex library, and probably to your chosen arbitrary precision type library.
4. The complete program to generate the constant half_pi
using function calculate_half_pi
is then:
#define USE_CPP_FLOAT // If required. #include <boost/math/constants/generate.hpp> int main() { BOOST_CONSTANTS_GENERATE(half_pi); }
The output from the program is a snippet of C++ code (actually a macro call)
that can be cut and pasted into boost/math/constants/constants.hpp
or else into your own code, for example:
BOOST_DEFINE_MATH_CONSTANT(half_pi, 1.570796326794896619231321691639751442e+00, "1.57079632679489661923132169163975144209858469968755291048747229615390820314310449931401741267105853399107404326e+00");
This macro BOOST_DEFINE_MATH_CONSTANT inserts a C++ struct code snippet that
declares the float
, double
and long
double
versions of the constant, plus
a decimal digit string representation correct to 100 decimal digits, and
all the meta-programming machinery needed to select between them.
The result of an expanded macro for Pi is shown below.
// Preprocessed pi constant, annotated. namespace boost { namespace math { namespace constants { namespace detail { template <class T> struct constant_pi { private: // Default implementations from string of decimal digits: static inline T get_from_string() { static const T result = detail::convert_from_string<T>("3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651e+00", boost::is_convertible<const char*, T>()); return result; } template <int N> static T compute(); public: // Default implementations from string of decimal digits: static inline T get(const mpl::int_<construct_from_string>&) { constant_initializer<T, & constant_pi<T>::get_from_string >::do_nothing(); return get_from_string(); } // Float, double and long double versions: static inline T get(const mpl::int_<construct_from_float>) { return 3.141592653589793238462643383279502884e+00F; } static inline T get(const mpl::int_<construct_from_double>&) { return 3.141592653589793238462643383279502884e+00; } static inline T get(const mpl::int_<construct_from_long_double>&) { return 3.141592653589793238462643383279502884e+00L; } // For very high precision that is nonetheless can be calculated at compile time: template <int N> static inline T get(const mpl::int_<N>& n) { constant_initializer2<T, N, & constant_pi<T>::template compute<N> >::do_nothing(); return compute<N>(); } //For true arbitrary precision, which may well vary at runtime. static inline T get(const mpl::int_<0>&) { return tools::digits<T>() > max_string_digits ? compute<0>() : get(mpl::int_<construct_from_string>()); } }; // template <class T> struct constant_pi } // namespace detail // The actual forwarding function (including policy to control precision). template <class T, class Policy> inline T pi( ) { return detail:: constant_pi<T>::get(typename construction_traits<T, Policy>::type()); } // The actual forwarding function (using default policy to control precision). template <class T> inline T pi() { return pi<T, boost::math::policies::policy<> >() } } // namespace constants // Namespace specific versions, for the three built-in floats: namespace float_constants { static const float pi = 3.141592653589793238462643383279502884e+00F; } namespace double_constants { static const double pi = 3.141592653589793238462643383279502884e+00; } namespace long_double_constants { static const long double pi = 3.141592653589793238462643383279502884e+00L; } namespace constants{; } // namespace constants } // namespace math } // namespace boost