...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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:
float
and the type is constructible
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
.
double
and the type is constructible
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
.
long double
and the type is constructible 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
.
__float128
(and the compiler
supports such a type) and the type is constructible 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
.
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.
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 |
---|---|
Boost.Math doesn't know how to control the internal precision of |
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 std::integral_constant<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. |
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 std::integral_constant<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
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_limitsspecialisation: no boost::math::policies::precision reports that there is no compile type precision available. boost::math::tools::digits () 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.