]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // Use, modification and distribution are subject to the |
2 | // Boost Software License, Version 1.0. | |
3 | // (See accompanying file LICENSE_1_0.txt | |
4 | // or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | // Copyright Paul A. Bristow 2019. | |
7 | // Copyright Christopher Kormanyos 2012. | |
8 | // Copyright John Maddock 2012. | |
9 | ||
10 | // This file is written to be included from a Quickbook .qbk document. | |
11 | // It can be compiled by the C++ compiler, and run. Any output can | |
12 | // also be added here as comment or included or pasted in elsewhere. | |
13 | // Caution: this file contains Quickbook markup as well as code | |
14 | // and comments: don't change any of the special comment markups! | |
15 | ||
16 | #ifdef _MSC_VER | |
17 | # pragma warning (disable : 4512) // assignment operator could not be generated. | |
18 | # pragma warning (disable : 4996) | |
19 | #endif | |
20 | ||
21 | //[big_seventh_example_1 | |
22 | ||
23 | /*`[h5 Using Boost.Multiprecision `cpp_float` types for numerical calculations with higher precision than __fundamental `long double`.] | |
24 | ||
25 | The Boost.Multiprecision library can be used for computations requiring precision | |
26 | exceeding that of standard __fundamental types such as `float`, `double` | |
27 | and `long double`. For extended-precision calculations, Boost.Multiprecision | |
28 | supplies several template data types called `cpp_bin_float_`. | |
29 | ||
30 | The number of decimal digits of precision is fixed at compile-time via template parameter. | |
31 | ||
32 | To use these floating-point types and | |
33 | [@https://www.boost.org/doc/libs/release/libs/math/doc/html/constants.html Boost.Math collection of high-precision constants], | |
34 | we need some includes: | |
35 | */ | |
36 | ||
37 | #include <boost/math/constants/constants.hpp> | |
38 | ||
39 | #include <boost/multiprecision/cpp_bin_float.hpp> | |
40 | // that includes some predefined typedefs like: | |
41 | // using boost::multiprecision::cpp_bin_float_quad; | |
42 | // using boost::multiprecision::cpp_bin_float_50; | |
43 | // using boost::multiprecision::cpp_bin_float_100; | |
44 | ||
45 | #include <iostream> | |
46 | #include <limits> | |
47 | ||
48 | /*` So now we can demonstrate with some trivial calculations: | |
49 | */ | |
50 | ||
51 | //] //[big_seventh_example_1] | |
52 | ||
53 | int main() | |
54 | { | |
55 | ||
56 | //[big_seventh_example_2 | |
57 | /*`Using a `typedef` like `cpp_bin_float_50` hides the complexity of multiprecision, and | |
58 | allows us to define variables with 50 decimal digit precision just like __fundamental `double`. | |
59 | */ | |
60 | using boost::multiprecision::cpp_bin_float_50; | |
61 | ||
62 | cpp_bin_float_50 seventh = cpp_bin_float_50(1) / 7; // 1 / 7 | |
63 | ||
64 | /*`By default, output would only show the standard 6 decimal digits, | |
65 | so set precision to show all 50 significant digits, including any trailing zeros (to show the full implied 50 digit precision). | |
66 | */ | |
67 | std::cout.precision(std::numeric_limits<cpp_bin_float_50>::digits10); // Show 50 decimal digit precision. | |
68 | std::cout << std::showpoint << std::endl; // Append any trailing zeros. | |
69 | std::cout << seventh << std::endl; | |
70 | /*`which outputs: | |
71 | ||
72 | 0.14285714285714285714285714285714285714285714285714 | |
73 | ||
74 | We can also use Boost.Math __math_constants like [pi], | |
75 | guaranteed to be initialized with the very last bit of precision for the floating-point type. | |
76 | */ | |
77 | std::cout << "pi = " << boost::math::constants::pi<cpp_bin_float_50>() << std::endl; | |
78 | cpp_bin_float_50 circumference = boost::math::constants::pi<cpp_bin_float_50>() * 2 * seventh; | |
79 | std::cout << "c = "<< circumference << std::endl; | |
80 | ||
81 | /*`which outputs | |
82 | ||
83 | pi = 3.1415926535897932384626433832795028841971693993751 | |
84 | ||
85 | c = 0.89759790102565521098932668093700082405633411410717 | |
86 | */ | |
87 | //] [/big_seventh_example_2] | |
88 | ||
89 | //[big_seventh_example_3 | |
90 | /*`So using `cpp_bin_float_50` looks like a simple 'drop-in' for the __fundamental_type like 'double', | |
91 | but beware of less-than-expected precision from construction or conversion from `double` or other lower precision types. | |
92 | This is a mistake that is very easy to make, | |
93 | and very difficult to detect because the difference in precision is only visible after about the 17th decimal digit. | |
94 | ||
95 | We can show this by constructing a fraction one seventh from `double`: | |
96 | */ | |
97 | cpp_bin_float_50 seventh_0 = cpp_bin_float_50(1/7); // Avoid the schoolboy-error `double d7 = 1 / 7;` giving zero! | |
98 | std::cout << "seventh_0 = " << seventh_0 << std::endl; | |
99 | // seventh_double0 = 0.0000000000000000000000000000000000000000000000000 | |
100 | ||
101 | cpp_bin_float_50 seventh_double = cpp_bin_float_50(1./7); // Construct from double! (0.14285714285714) | |
102 | std::cout << "seventh_double = " << seventh_double << std::endl; // Boost.Multiprecision post-school error! | |
103 | // seventh_double = 0.14285714285714284921269268124888185411691665649414 | |
104 | ||
105 | /*`Did you spot the probably-unintended difference? After the 17th decimal digit, result is apparently random | |
106 | and not the expected recurring pattern 14285714285714... | |
107 | ||
108 | The 'random' digits after digit 17 are from the `cpp_bin_float_50` representation of the `double` value 0.14285714285714 | |
109 | which is different from the 'better' `cpp_bin_float_50` representation of the fraction 1/7 | |
110 | */ | |
111 | ||
112 | cpp_bin_float_50 seventh_big(1); // 1 | |
113 | seventh_big /= 7; | |
114 | std::cout << "seventh_big = " << seventh_big << std::endl; // | |
115 | // seventh_big = 0.14285714285714285714285714285714285714285714285714 | |
116 | //`Note the recurring 14285714285714 pattern as expected. | |
117 | ||
118 | //`We can also construct a `const` version (but not yet `constexpr`) and evaluate in a single expression: | |
119 | ||
120 | const cpp_bin_float_50 seventh_const (cpp_bin_float_50(1) / 7); | |
121 | std::cout << "seventh_const = " << seventh_const << std::endl; // | |
122 | // seventh_const = 0.14285714285714285714285714285714285714285714285714 | |
123 | //] [/big_seventh_example_3] | |
124 | ||
125 | //[big_seventh_example_constexpr | |
126 | ||
127 | // Sadly we cannot (yet) write: | |
128 | // constexpr cpp_bin_float_50 any_constexpr(0); | |
129 | ||
130 | // constexpr cpp_bin_float_50 seventh_constexpr (cpp_bin_float_50(1) / 7); | |
131 | // std::cout << "seventh_constexpr = " << seventh_constexpr << std::endl; // | |
1e59de90 TL |
132 | // nor use the macro constexpr unless it returns `const` |
133 | // constexpr cpp_bin_float_50 seventh_constexpr(seventh_const); | |
f67539c2 TL |
134 | |
135 | //] [/big_seventh_example_constexpr] | |
136 | ||
137 | //[big_seventh_example_4 | |
138 | /*`For some purposes, this difference in precision may be insignificant, | |
139 | but if one is implementing a formula involving a fraction from integers, | |
140 | including decimal fractions like 1/10, 1/100, then comparison with other computations like __WolframAlpha | |
141 | will reveal differences whose cause may be perplexing. | |
142 | ||
143 | To get as precise-as-possible decimal fractions like 1.234, we can write | |
144 | */ | |
145 | ||
146 | const cpp_bin_float_50 f1(cpp_bin_float_50(1234) / 1000); // Construct from a fraction. | |
147 | std::cout << "cpp_bin_float_50 f1(cpp_bin_float_50(1234) / 1000) = " << f1 << std::endl; // cpp_bin_float_50 f1(cpp_bin_float_50(1234) / 1000) = 1.2340000000000000000000000000000000000000000000000 | |
148 | /*`or | |
149 | */ | |
150 | const cpp_bin_float_50 f2("1.234"); // Construct from decimal digit string. | |
151 | std::cout << "cpp_bin_float_50 f2(\"1.234\") = " << f2 << std::endl; // cpp_bin_float_50 f2("1.234") = 1.2340000000000000000000000000000000000000000000000 | |
152 | ||
153 | /*`that are different from constructing from a `double` with value 1.234 | |
154 | */ | |
155 | const cpp_bin_float_50 f3(cpp_bin_float_50(1.234)); | |
156 | std::cout << "cpp_bin_float_50 f3(cpp_bin_float_50(1.234)) = " << f3 << std::endl; // 1.2339999999999999857891452847979962825775146484375 | |
157 | ||
158 | //] [/big_seventh_example_4] | |
159 | ||
160 | return 0; | |
161 | } // int main() | |
162 | ||
163 | ||
164 | /* | |
165 | //[big_seventh_example_output | |
166 | ||
167 | 0.14285714285714285714285714285714285714285714285714 | |
168 | pi = 3.1415926535897932384626433832795028841971693993751 | |
169 | c = 0.89759790102565521098932668093700082405633411410717 | |
170 | seventh_0 = 0.0000000000000000000000000000000000000000000000000 | |
171 | seventh_double = 0.14285714285714284921269268124888185411691665649414 | |
172 | seventh_big = 0.14285714285714285714285714285714285714285714285714 | |
173 | seventh_const = 0.14285714285714285714285714285714285714285714285714 | |
174 | cpp_bin_float_50 f1(cpp_bin_float_50(1234) / 100) = 12.340000000000000000000000000000000000000000000000 | |
175 | cpp_bin_float_50 f2("1.234") = 1.2340000000000000000000000000000000000000000000000 | |
176 | cpp_bin_float_50 f3(cpp_bin_float_50(1.234)) = 1.2339999999999999857891452847979962825775146484375 | |
177 | ||
178 | //] //[/big_seventh_example_output] | |
179 | ||
180 | */ | |
181 |