File: tutorial_constexpr.qbk

package info (click to toggle)
boost1.83 1.83.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 545,632 kB
  • sloc: cpp: 3,857,086; xml: 125,552; ansic: 34,414; python: 25,887; asm: 5,276; sh: 4,799; ada: 1,681; makefile: 1,629; perl: 1,212; pascal: 1,139; sql: 810; yacc: 478; ruby: 102; lisp: 24; csh: 6
file content (179 lines) | stat: -rw-r--r-- 8,849 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
[/
  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]