Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. — Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Use With User-Defined Types
PrevUpHomeNext

The most common example of a high-precision user-defined type will probably be Boost.Multiprecision.

The syntax for using the function-call constants with user-defined types is the same as it is in the template class, which is to say we use:

#include <boost/math/constants/constants.hpp>

boost::math::constants::pi<UserDefinedType>();

For example:

boost::math::constants::pi<boost::multiprecision::cpp_dec_float_50>();

giving π with a precision of 50 decimal digits.

However, since the precision of the user-defined type may be much greater than that of the built-in floating point types, how the value returned is created is as follows:

  • If the precision of the type is known at compile time:
    • If the precision is less than or equal to that of a float and the type is constructable from a float then our code returns a float literal. If the user-defined type is a literal type then the function call that returns the constant will be a constexp.
    • If the precision is less than or equal to that of a double and the type is constructable from a double then our code returns a double literal. If the user-defined type is a literal type then the function call that returns the constant will be a constexp.
    • If the precision is less than or equal to that of a long double and the type is constructable from a long double then our code returns a long double literal. If the user-defined type is a literal type then the function call that returns the constant will be a constexp.
    • If the precision is less than or equal to that of a __float128 (and the compiler supports such a type) and the type is constructable from a __float128 then our code returns a __float128 literal. If the user-defined type is a literal type then the function call that returns the constant will be a constexp.
    • If the precision is less than 100 decimal digits, then the constant will be constructed (just the once, then cached in a thread-safe manner) from a string representation of the constant. In this case the value is returned as a const reference to the cached value.
    • Otherwise the value is computed (just once, then cached in a thread-safe manner). In this case the value is returned as a const reference to the cached value.
  • If the precision is unknown at compile time then:
    • If the runtime precision (obtained from a call to boost::math::tools::digits<T>()) is less than 100 decimal digits, then the constant is constructed "on the fly" from the string representation of the constant.
    • Otherwise the value is constructed "on the fly" by calculating then value of the constant using the current default precision of the type. Note that this can make use of the constants rather expensive.

In addition, it is possible to pass a Policy type as a second template argument, and use this to control the precision:

#include <boost/math/constants/constants.hpp>

typedef boost::math::policies::policy<boost::math::policies::digits2<80> > my_policy_type;
boost::math::constants::pi<MyType, my_policy_type>();
[Note] Note

Boost.Math doesn't know how to control the internal precision of MyType, the policy just controls how the selection process above is carried out, and the calculation precision if the result is computed.

It is also possible to control which method is used to construct the constant by specialising the traits class construction_traits:

namespace boost{ namespace math{ namespace constant{

template <class T, class Policy>
struct construction_traits
{
   typedef mpl::int_<N> type;
};

}}} // namespaces

Where N takes one of the following values:

N

Meaning

0

The precision is unavailable at compile time; either construct from a decimal digit string or calculate on the fly depending upon the runtime precision.

1

Return a float precision constant.

2

Return a double precision constant.

3

Return a long double precision constant.

4

Construct the result from the string representation, and cache the result.

Any other value N

Sets the compile time precision to N bits.

Custom Specializing a constant

In addition, for user-defined types that need special handling, it's possible to [partially-] specialize the internal structure used by each constant. For example, suppose we're using the C++ wrapper around MPFR mpfr_class: this has its own representation of Pi which we may well wish to use in place of the above mechanism. We can achieve this by specialising the class template boost::math::constants::detail::constant_pi:

namespace boost{ namespace math{ namespace constants{ namespace detail{

template<>
struct constant_pi<mpfr_class>
{
   template<int N>
   static mpfr_class get(const mpl::int_<N>&)
   {
      // The template param N is one of the values in the table above,
      // we can either handle all cases in one as is the case here,
      // or overload "get" for the different options.
      mpfr_class result;
      mpfr_const_pi(result.get_mpfr_t(), GMP_RNDN);
      return result;
   }
};

}}}} // namespaces
Diagnosing what meta-programmed code is doing

Finally, since it can be tricky to diagnose what meta-programmed code is doing, there is a diagnostic routine that prints information about how this library will handle a specific type, it can be used like this:

#include <boost/math/constants/info.hpp>

int main()
{
   boost::math::constants::print_info_on_type<MyType>();
}

If you wish, you can also pass an optional std::ostream argument to the print_info_on_type function. Typical output for a user-defined type looks like this:

Information on the Implementation and Handling of
Mathematical Constants for Type class boost::math::concepts::real_concept

Checking for std::numeric_limits<class boost::math::concepts::real_concept> specialisation: no
boost::math::policies::precision<class boost::math::concepts::real_concept, Policy>
reports that there is no compile type precision available.
boost::math::tools::digits<class boost::math::concepts::real_concept>()
reports that the current runtime precision is
53 binary digits.
No compile time precision is available, the construction method
will be decided at runtime and results will not be cached
- this may lead to poor runtime performance.
Current runtime precision indicates that
the constant will be constructed from a string on each call.

PrevUpHomeNext