180 lines
8.6 KiB
Plaintext
180 lines
8.6 KiB
Plaintext
[/
|
|
Copyright 2011 - 2020 John Maddock.
|
|
Copyright 2013 - 2019 Paul A. Bristow.
|
|
Copyright 2013 Christopher Kormanyos.
|
|
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt).
|
|
]
|
|
|
|
[section:lits Literal Types and `constexpr` Support]
|
|
|
|
There are two kinds of `constexpr` support in this library:
|
|
|
|
* The more basic version requires only C++11 and allow the construction of some number types as literals.
|
|
* The more advanced support permits constexpr arithmetic and requires at least C++14
|
|
constexpr support, and for many operations C++2a support
|
|
|
|
[h4 Declaring numeric literals]
|
|
|
|
There are two backend types which are literals:
|
|
|
|
* __float128__ (which requires GCC), and
|
|
* Instantiations of `cpp_int_backend` where the Allocator parameter is type `void`.
|
|
In addition, prior to C++14 the Checked parameter must be `boost::multiprecision::unchecked`.
|
|
|
|
For example:
|
|
|
|
using namespace boost::multiprecision;
|
|
|
|
constexpr float128 f = 0.1Q // OK, float128's are always literals in C++11
|
|
|
|
constexpr int128_t i = 0; // OK, fixed precision int128_t has no allocator.
|
|
constexpr uint1024_t j = 0xFFFFFFFF00000000uLL; // OK, fixed precision uint1024_t has no allocator.
|
|
|
|
constexpr checked_uint128_t k = 1; // OK from C++14 and later, not supported for C++11.
|
|
constexpr checked_uint128_t k = -1; // Error, as this would normally lead to a runtime failure (exception).
|
|
constexpr cpp_int l = 2; // Error, type is not a literal as it performs memory management.
|
|
|
|
There is also support for user defined-literals with __cpp_int - these are limited to unchecked, fixed precision `cpp_int`'s
|
|
which are specified in hexadecimal notation. The suffixes supported are:
|
|
|
|
[table
|
|
[[Suffix][Meaning]]
|
|
[[_cppi][Specifies a value of type: `number<cpp_int_backend<N,N,signed_magnitude,unchecked,void> >`, where N is chosen
|
|
to contain just enough digits to hold the number specified.]]
|
|
[[_cppui][Specifies a value of type: `number<cpp_int_backend<N,N,unsigned_magnitude,unchecked,void> >`, where N is chosen
|
|
to contain just enough digits to hold the number specified.]]
|
|
[[_cppi['N]][Specifies a value of type `number<cpp_int_backend<N,N,signed_magnitude,unchecked,void> >`.]]
|
|
[[_cppui['N]][Specifies a value of type `number<cpp_int_backend<N,N,signed_magnitude,unchecked,void> >`.]]
|
|
]
|
|
|
|
In each case, use of these suffixes with hexadecimal values produces a `constexpr` result.
|
|
|
|
Examples:
|
|
|
|
// Any use of user defined literals requires that we import the literal-operators into current scope first:
|
|
using namespace boost::multiprecision::literals;
|
|
//
|
|
// To keep things simple in the example, we'll make our types used visible to this scope as well:
|
|
using namespace boost::multiprecision;
|
|
//
|
|
// The value zero as a number<cpp_int_backend<4,4,signed_magnitude,unchecked,void> >:
|
|
constexpr auto a = 0x0_cppi;
|
|
// The type of each constant has 4 bits per hexadecimal digit,
|
|
// so this is of type uint256_t (ie number<cpp_int_backend<256,256,unsigned_magnitude,unchecked,void> >):
|
|
constexpr auto b = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui;
|
|
//
|
|
// Smaller values can be assigned to larger values:
|
|
int256_t c = 0x1234_cppi; // OK
|
|
//
|
|
// However, this only works in constexpr contexts from C++14 onwards:
|
|
constexpr int256_t d = 0x1_cppi; // Compiler error in C++11, requires C++14
|
|
//
|
|
// Constants can be padded out with leading zeros to generate wider types:
|
|
constexpr uint256_t e = 0x0000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFF_cppui; // OK
|
|
//
|
|
// However, specific-width types are best produced with specific-width suffixes,
|
|
// ones supported by default are `_cpp[u]i128`, `_cpp[u]i256`, `_cpp[u]i512`, `_cpp[u]i1024`.
|
|
//
|
|
constexpr int128_t f = 0x1234_cppi128; // OK, always produces an int128_t as the result.
|
|
constexpr uint1024_t g = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccc_cppui1024; // OK,
|
|
// always produces an uint1024_t as the result.
|
|
//
|
|
// If other specific-width types are required, then there is a macro for generating the operators for these.
|
|
// The macro can be used at namespace scope only:
|
|
//
|
|
BOOST_MP_DEFINE_SIZED_CPP_INT_LITERAL(2048);
|
|
//
|
|
// Now we can create 2048-bit literals as well:
|
|
constexpr auto h = 0xff_cppi2048; // h is of type number<cpp_int_backend<2048,2048,signed_magnitude,unchecked,void> >
|
|
//
|
|
// Finally, negative values are handled via the unary minus operator:
|
|
//
|
|
constexpr int1024_t i = -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_cppui1024;
|
|
//
|
|
// Which means this also works:
|
|
constexpr int1024_t j = -g; // OK: unary minus operator is constexpr.
|
|
|
|
[h4 constexpr arithmetic]
|
|
|
|
The front end of the library is all `constexpr` from C++14 and later. Currently there are only two
|
|
backend types that are `constexpr` aware: __float128 and __cpp_int. More backends will follow at a later date.
|
|
|
|
Provided the compiler is GCC, type __float128__ support `constexpr` operations on all arithmetic operations from C++14, comparisons,
|
|
`abs`, `fabs`, `fpclassify`, `isnan`, `isinf`, `isfinite` and `isnormal` are also fully supported, but the transcendental functions are not.
|
|
|
|
The __cpp_int types support constexpr arithmetic, provided it is a fixed precision type with no allocator. It may also
|
|
be a checked integer: in which case a compiler error will be generated on overflow or undefined behaviour. In addition
|
|
the free functions `abs`, `swap`, `multiply`, `add`, `subtract`, `divide_qr`, `integer_modulus`, `powm`, `lsb`, `msb`,
|
|
`bit_test`, `bit_set`, `bit_unset`, `bit_flip`, `sqrt`, `gcd`, `lcm` are all supported. Use of __cpp_int in this way
|
|
requires either a C++2a compiler (one which supports `std::is_constant_evaluated()` - currently only gcc-9 or clang-9 or later),
|
|
or GCC-6 or later in C++14 mode.
|
|
Compilers other than GCC and without `std::is_constant_evaluated()` will support a very limited set of operations:
|
|
expect to hit roadblocks rather easily.
|
|
|
|
See __compiler_support for __is_constant_evaluated;
|
|
|
|
For example given:
|
|
|
|
[constexpr_circle]
|
|
|
|
We can now calculate areas and circumferences, using all compile-time `constexpr` arithmetic:
|
|
|
|
[constexpr_circle_usage]
|
|
|
|
Note that these make use of the numeric constants from the __math_constants library, which also happen to be `constexpr`.
|
|
These usually have the full precision of the floating-point type, here 128-bit, about 36 decimal digits.
|
|
|
|
[h5:hermite_poly_coeffics Calculating Hermite Polynomial coefficients at compile time]
|
|
|
|
For a more interesting example, in [@../../example/constexpr_float_arithmetic_examples.cpp constexpr_float_arithmetic_examples.cpp]
|
|
we define a simple class for `constexpr` polynomial arithmetic:
|
|
|
|
template <class T, unsigned Order>
|
|
struct const_polynomial;
|
|
|
|
Given this, we can use recurrence relations to calculate the coefficients for various orthogonal
|
|
polynomials - in the example we use the Hermite polynomials. Only the constructor does any work -
|
|
it uses the recurrence relations to calculate the coefficient array:
|
|
|
|
[hermite_example]
|
|
|
|
Now we just need to define ['H[sub 0]] and ['H[sub 1]] as termination conditions for the recurrence:
|
|
|
|
[hermite_example2]
|
|
|
|
We can now declare ['H[sub 9]] as a `constexpr` object, access the coefficients, and evaluate
|
|
at an abscissa value, all at compile-time using `constexpr` arithmetic:
|
|
|
|
[hermite_example3]
|
|
|
|
See [@../../example/constexpr_float_arithmetic_examples.cpp constexpr_float_arithmetic_examples.cpp] for working code.
|
|
|
|
Also since the coefficients to the Hermite polynomials are integers, we can also generate the Hermite
|
|
coefficients using (fixed precision) `cpp_int`s: see [@../../test/constexpr_test_cpp_int_6.cpp constexpr_test_cpp_int_6.cpp].
|
|
|
|
[h5:factorial_constexpr `constexpr` Factorials]
|
|
|
|
We can also generate integer factorials in [@../../test/constexpr_test_cpp_int_5.cpp constexpr_test_cpp_int_5.cpp] like so:
|
|
|
|
[factorial_decl]
|
|
|
|
and validate the result:
|
|
|
|
constexpr uint1024_t f1 = factorial(uint1024_t(31)); // Factorial 31!
|
|
static_assert(f1 == 0x1956ad0aae33a4560c5cd2c000000_cppi); // Expected result as an Boost.Multiprecision integer literal.
|
|
|
|
[h5:random_constexpr Random `constexpr` values]
|
|
|
|
Another example in [@../../test/constexpr_test_cpp_int_7.cpp constexpr_test_cpp_int_7.cpp] generates
|
|
a fresh multiprecision random number each time the file is compiled. It includes an C++ template implementation of the
|
|
[@https://en.wikipedia.org/wiki/KISS_(algorithm) KISS random number algorithm by George Marsaglia] for `cpp_int` integers.
|
|
|
|
[random_constexpr_cppint]
|
|
|
|
See also the __random section.
|
|
|
|
[endsect] [/section:lits Literal Types and `constexpr` Support]
|