lambert_w_precision_example.cpp 13 KB

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