1 // Copyright Paul A. Bristow 2016, 2018.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or
5 // copy at http ://www.boost.org/LICENSE_1_0.txt).
7 //! Lambert W examples of controlling precision
9 // #define BOOST_MATH_INSTRUMENT_LAMBERT_W // #define only for (much) diagnostic output.
11 #include <boost/config.hpp> // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ...
12 #include <boost/version.hpp> // for BOOST_MSVC versions.
13 #include <boost/cstdint.hpp>
14 #include <boost/exception/exception.hpp> // boost::exception
15 #include <boost/math/constants/constants.hpp> // For exp_minus_one == 3.67879441171442321595523770161460867e-01.
16 #include <boost/math/policies/policy.hpp>
17 #include <boost/math/special_functions/next.hpp> // for float_distance.
18 #include <boost/math/special_functions/relative_difference.hpp> // for relative and epsilon difference.
20 // Built-in/fundamental GCC float128 or Intel Quad 128-bit type, if available.
21 #ifdef BOOST_HAS_FLOAT128
22 #include <boost/multiprecision/float128.hpp> // Not available for MSVC.
23 // sets BOOST_MP_USE_FLOAT128 for GCC
24 using boost::multiprecision::float128
;
25 #endif //# NOT _MSC_VER
27 #include <boost/multiprecision/cpp_dec_float.hpp> // boost::multiprecision::cpp_dec_float_50
28 using boost::multiprecision::cpp_dec_float_50
; // 50 decimal digits type.
29 using boost::multiprecision::cpp_dec_float_100
; // 100 decimal digits type.
31 #include <boost/multiprecision/cpp_bin_float.hpp>
32 using boost::multiprecision::cpp_bin_float_double_extended
;
33 using boost::multiprecision::cpp_bin_float_double
;
34 using boost::multiprecision::cpp_bin_float_quad
;
35 // For lambert_w function.
36 #include <boost/math/special_functions/lambert_w.hpp>
37 // using boost::math::lambert_w0;
38 // using boost::math::lambert_wm1;
44 #include <limits> // For std::numeric_limits.
50 std::cout
<< "Lambert W examples of precision control." << std::endl
;
51 std::cout
.precision(std::numeric_limits
<double>::max_digits10
);
52 std::cout
<< std::showpoint
<< std::endl
; // Show any trailing zeros.
54 using boost::math::constants::exp_minus_one
;
56 using boost::math::lambert_w0
;
57 using boost::math::lambert_wm1
;
59 // Error handling policy examples.
60 using namespace boost::math::policies
;
61 using boost::math::policies::make_policy
;
62 using boost::math::policies::policy
;
63 using boost::math::policies::evaluation_error
;
64 using boost::math::policies::domain_error
;
65 using boost::math::policies::overflow_error
;
66 using boost::math::policies::throw_on_error
;
68 //[lambert_w_precision_reference_w
70 using boost::multiprecision::cpp_bin_float_50
;
71 using boost::math::float_distance
;
73 cpp_bin_float_50
z("10."); // Note use a decimal digit string, not a double 10.
75 std::cout
.precision(std::numeric_limits
<cpp_bin_float_50
>::digits10
);
77 r
= lambert_w0(z
); // Default policy.
78 std::cout
<< "lambert_w0(z) cpp_bin_float_50 = " << r
<< std::endl
;
79 //lambert_w0(z) cpp_bin_float_50 = 1.7455280027406993830743012648753899115352881290809
80 // [N[productlog[10], 50]] == 1.7455280027406993830743012648753899115352881290809
81 std::cout
.precision(std::numeric_limits
<double>::max_digits10
);
82 std::cout
<< "lambert_w0(z) static_cast from cpp_bin_float_50 = "
83 << static_cast<double>(r
) << std::endl
;
84 // double lambert_w0(z) static_cast from cpp_bin_float_50 = 1.7455280027406994
85 // [N[productlog[10], 17]] == 1.7455280027406994
86 std::cout
<< "bits different from Wolfram = "
87 << static_cast<int>(float_distance(static_cast<double>(r
), 1.7455280027406994))
91 //] [/lambert_w_precision_reference_w]
93 //[lambert_w_precision_0
94 std::cout
.precision(std::numeric_limits
<float>::max_digits10
); // Show all potentially significant decimal digits,
95 std::cout
<< std::showpoint
<< std::endl
; // and show any significant trailing zeros too.
98 std::cout
<< "Lambert W (" << x
<< ") = " << lambert_w0(x
) << std::endl
;
99 //] [/lambert_w_precision_0]
102 //[lambert_w_precision_output_0
103 Lambert W (10.0000000) = 1.74552800
104 //] [/lambert_w_precision_output_0]
106 { // Lambert W0 Halley step example
107 //[lambert_w_precision_1
108 using boost::math::lambert_w_detail::lambert_w_halley_step
;
109 using boost::math::epsilon_difference
;
110 using boost::math::relative_difference
;
112 std::cout
<< std::showpoint
<< std::endl
; // and show any significant trailing zeros too.
113 std::cout
.precision(std::numeric_limits
<double>::max_digits10
); // 17 decimal digits for double.
115 cpp_bin_float_50
z50("1.23"); // Note: use a decimal digit string, not a double 1.23!
116 double z
= static_cast<double>(z50
);
117 cpp_bin_float_50 w50
;
118 w50
= lambert_w0(z50
);
119 std::cout
.precision(std::numeric_limits
<cpp_bin_float_50
>::max_digits10
); // 50 decimal digits.
120 std::cout
<< "Reference Lambert W (" << z
<< ") =\n "
122 std::cout
.precision(std::numeric_limits
<double>::max_digits10
); // 17 decimal digits for double.
123 double wr
= static_cast<double>(w50
);
124 std::cout
<< "Reference Lambert W (" << z
<< ") = " << wr
<< std::endl
;
126 double w
= lambert_w0(z
);
127 std::cout
<< "Rat/poly Lambert W (" << z
<< ") = " << lambert_w0(z
) << std::endl
;
128 // Add a Halley step to the value obtained from rational polynomial approximation.
129 double ww
= lambert_w_halley_step(lambert_w0(z
), z
);
130 std::cout
<< "Halley Step Lambert W (" << z
<< ") = " << lambert_w_halley_step(lambert_w0(z
), z
) << std::endl
;
132 std::cout
<< "absolute difference from Halley step = " << w
- ww
<< std::endl
;
133 std::cout
<< "relative difference from Halley step = " << relative_difference(w
, ww
) << std::endl
;
134 std::cout
<< "epsilon difference from Halley step = " << epsilon_difference(w
, ww
) << std::endl
;
135 std::cout
<< "epsilon for float = " << std::numeric_limits
<double>::epsilon() << std::endl
;
136 std::cout
<< "bits different from Halley step = " << static_cast<int>(float_distance(w
, ww
)) << std::endl
;
137 //] [/lambert_w_precision_1]
141 //[lambert_w_precision_output_1
142 Reference Lambert W (1.2299999999999999822364316059974953532218933105468750) =
143 0.64520356959320237759035605255334853830173300262666480
144 Reference Lambert W (1.2300000000000000) = 0.64520356959320235
145 Rat/poly Lambert W (1.2300000000000000) = 0.64520356959320224
146 Halley Step Lambert W (1.2300000000000000) = 0.64520356959320235
147 absolute difference from Halley step = -1.1102230246251565e-16
148 relative difference from Halley step = 1.7207329236029286e-16
149 epsilon difference from Halley step = 0.77494921535422934
150 epsilon for float = 2.2204460492503131e-16
151 bits different from Halley step = 1
152 //] [/lambert_w_precision_output_1]
155 } // Lambert W0 Halley step example
157 { // Lambert W-1 Halley step example
158 //[lambert_w_precision_2
159 using boost::math::lambert_w_detail::lambert_w_halley_step
;
160 using boost::math::epsilon_difference
;
161 using boost::math::relative_difference
;
163 std::cout
<< std::showpoint
<< std::endl
; // and show any significant trailing zeros too.
164 std::cout
.precision(std::numeric_limits
<double>::max_digits10
); // 17 decimal digits for double.
166 cpp_bin_float_50
z50("-0.123"); // Note: use a decimal digit string, not a double -1.234!
167 double z
= static_cast<double>(z50
);
168 cpp_bin_float_50 wm1_50
;
169 wm1_50
= lambert_wm1(z50
);
170 std::cout
.precision(std::numeric_limits
<cpp_bin_float_50
>::max_digits10
); // 50 decimal digits.
171 std::cout
<< "Reference Lambert W-1 (" << z
<< ") =\n "
172 << wm1_50
<< std::endl
;
173 std::cout
.precision(std::numeric_limits
<double>::max_digits10
); // 17 decimal digits for double.
174 double wr
= static_cast<double>(wm1_50
);
175 std::cout
<< "Reference Lambert W-1 (" << z
<< ") = " << wr
<< std::endl
;
177 double w
= lambert_wm1(z
);
178 std::cout
<< "Rat/poly Lambert W-1 (" << z
<< ") = " << lambert_wm1(z
) << std::endl
;
179 // Add a Halley step to the value obtained from rational polynomial approximation.
180 double ww
= lambert_w_halley_step(lambert_wm1(z
), z
);
181 std::cout
<< "Halley Step Lambert W (" << z
<< ") = " << lambert_w_halley_step(lambert_wm1(z
), z
) << std::endl
;
183 std::cout
<< "absolute difference from Halley step = " << w
- ww
<< std::endl
;
184 std::cout
<< "relative difference from Halley step = " << relative_difference(w
, ww
) << std::endl
;
185 std::cout
<< "epsilon difference from Halley step = " << epsilon_difference(w
, ww
) << std::endl
;
186 std::cout
<< "epsilon for float = " << std::numeric_limits
<double>::epsilon() << std::endl
;
187 std::cout
<< "bits different from Halley step = " << static_cast<int>(float_distance(w
, ww
)) << std::endl
;
188 //] [/lambert_w_precision_2]
191 //[lambert_w_precision_output_2
192 Reference Lambert W-1 (-0.12299999999999999822364316059974953532218933105468750) =
193 -3.2849102557740360179084675531714935199110302996513384
194 Reference Lambert W-1 (-0.12300000000000000) = -3.2849102557740362
195 Rat/poly Lambert W-1 (-0.12300000000000000) = -3.2849102557740357
196 Halley Step Lambert W (-0.12300000000000000) = -3.2849102557740362
197 absolute difference from Halley step = 4.4408920985006262e-16
198 relative difference from Halley step = 1.3519066740696092e-16
199 epsilon difference from Halley step = 0.60884463935795785
200 epsilon for float = 2.2204460492503131e-16
201 bits different from Halley step = -1
202 //] [/lambert_w_precision_output_2]
207 // Similar example using cpp_bin_float_quad (128-bit floating-point types).
209 cpp_bin_float_quad zq
= 10.;
210 std::cout
<< "\nTest evaluation of cpp_bin_float_quad Lambert W(" << zq
<< ")"
212 std::cout
<< std::setprecision(3) << "std::numeric_limits<cpp_bin_float_quad>::digits = " << std::numeric_limits
<cpp_bin_float_quad
>::digits
<< std::endl
;
213 std::cout
<< std::setprecision(3) << "std::numeric_limits<cpp_bin_float_quad>::epsilon() = " << std::numeric_limits
<cpp_bin_float_quad
>::epsilon() << std::endl
;
214 std::cout
<< std::setprecision(3) << "std::numeric_limits<cpp_bin_float_quad>::max_digits10 = " << std::numeric_limits
<cpp_bin_float_quad
>::max_digits10
<< std::endl
;
215 std::cout
<< std::setprecision(3) << "std::numeric_limits<cpp_bin_float_quad>::digits10 = " << std::numeric_limits
<cpp_bin_float_quad
>::digits10
<< std::endl
;
216 std::cout
.precision(std::numeric_limits
<cpp_bin_float_quad
>::max_digits10
);
217 // All are same precision because double precision first approximation used before Halley.
223 { // Reference value for lambert_w0(10)
224 cpp_dec_float_50
z("10");
226 std::cout
.precision(std::numeric_limits
<cpp_dec_float_50
>::digits10
);
228 r
= lambert_w0(z
); // Default policy.
229 std::cout
<< "lambert_w0(z) cpp_dec_float_50 = " << r
<< std::endl
; // 0.56714329040978387299996866221035554975381578718651
230 std::cout
.precision(std::numeric_limits
<cpp_bin_float_quad
>::max_digits10
);
232 std::cout
<< "lambert_w0(z) cpp_dec_float_50 cast to quad (max_digits10(" << std::numeric_limits
<cpp_bin_float_quad
>::max_digits10
<<
233 " ) = " << static_cast<cpp_bin_float_quad
>(r
) << std::endl
; // 1.7455280027406993830743012648753899115352881290809
234 std::cout
.precision(std::numeric_limits
<cpp_bin_float_quad
>::digits10
); // 1.745528002740699383074301264875389837
235 std::cout
<< "lambert_w0(z) cpp_dec_float_50 cast to quad (digits10(" << std::numeric_limits
<cpp_bin_float_quad
>::digits10
<<
236 " ) = " << static_cast<cpp_bin_float_quad
>(r
) << std::endl
; // 1.74552800274069938307430126487539
237 std::cout
.precision(std::numeric_limits
<cpp_bin_float_quad
>::digits10
+ 1); //
239 std::cout
<< "lambert_w0(z) cpp_dec_float_50 cast to quad (digits10(" << std::numeric_limits
<cpp_bin_float_quad
>::digits10
<<
240 " ) = " << static_cast<cpp_bin_float_quad
>(r
) << std::endl
; // 1.74552800274069938307430126487539
242 // [N[productlog[10], 50]] == 1.7455280027406993830743012648753899115352881290809
244 // [N[productlog[10], 37]] == 1.745528002740699383074301264875389912
245 // [N[productlog[10], 34]] == 1.745528002740699383074301264875390
246 // [N[productlog[10], 33]] == 1.74552800274069938307430126487539
248 // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.745528002740699383074301264875389837
250 // lambert_w0(z) cpp_dec_float_50 = 1.7455280027406993830743012648753899115352881290809
251 // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.745528002740699383074301264875389837
252 // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.74552800274069938307430126487539
255 catch (std::exception
& ex
)
257 std::cout
<< ex
.what() << std::endl
;