Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Defining New Constants

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((boost::integral_constant<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((boost::integral_constant<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] Note

Previously defined constants like pi and e can be used, but by not simply calling pi<T>(); specifying the precision via the policy pi<T, policies::policy<policies::digits2<N> > >() is essential to ensure full accuracy.

[Warning] Warning

Newly defined constants can only be used once they are included in boost/math/constants/constants.hpp. So if you add template <class T, class N> T constant_my_constant{...}, then you cannot define constant_my_constant until you add the temporary BOOST_DEFINE_MATH_CONSTANT(my_constant, 0.0, "0"). Failing to do this will result in surprising compile errors:

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. You need to add libs\math\include_private to your compiler's include path as the needed header is not installed in the usual places by default (this avoids a cyclic dependency between the Math and Multiprecision library's headers).

5. 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 boost::integral_constant<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 boost::integral_constant<int, construct_from_float>)
            {
              return 3.141592653589793238462643383279502884e+00F;
            }
            static inline  T get(const boost::integral_constant<int, construct_from_double>&)
            {
              return 3.141592653589793238462643383279502884e+00;
            }
            static inline  T get(const boost::integral_constant<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 boost::integral_constant<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 boost::integral_constant<int, 0>&)
            {
              return tools::digits<T>() > max_string_digits ? compute<0>() : get(boost::integral_constant<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

PrevUpHomeNext