lambert_w_simple_examples.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright Paul A. Bristow 2016, 2017.
  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. // Build and run a simple examples of Lambert W function.
  6. // Some macros that will show some(or much) diagnostic values if #defined.
  7. //#define-able macros
  8. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W0 // W0 branch diagnostics.
  9. //#define BOOST_MATH_INSTRUMENT_LAMBERT_Wm1 // W1 branch diagnostics.
  10. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY // Halley refinement diagnostics.
  11. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER // Schroeder refinement diagnostics.
  12. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS // Number of terms used for near-singularity series.
  13. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN // higher than built-in precision types approximation and refinement.
  14. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES // Show evaluation of series near branch singularity.
  15. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of series for small z.
  16. //#define BOOST_MATH_INSTRUMENT_LAMBERT_W0_LOOKUP // Show results from lookup table.
  17. #include <boost/config.hpp> // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ...
  18. #include <boost/version.hpp> // for BOOST_MSVC versions.
  19. #include <boost/cstdint.hpp>
  20. #include <boost/exception/exception.hpp> // boost::exception
  21. #include <boost/math/constants/constants.hpp> // For exp_minus_one == 3.67879441171442321595523770161460867e-01.
  22. #include <boost/math/policies/policy.hpp>
  23. #include <boost/multiprecision/cpp_dec_float.hpp> // boost::multiprecision::cpp_dec_float_50
  24. using boost::multiprecision::cpp_dec_float_50; // 50 decimal digits type.
  25. using boost::multiprecision::cpp_dec_float_100; // 100 decimal digits type.
  26. using boost::multiprecision::backends::cpp_dec_float;
  27. using boost::multiprecision::number;
  28. typedef number<cpp_dec_float<1000> > cpp_dec_float_1000; // 1000 decimal digit types
  29. #include <boost/multiprecision/cpp_bin_float.hpp>
  30. using boost::multiprecision::cpp_bin_float_double; // == double
  31. using boost::multiprecision::cpp_bin_float_double_extended; // 80-bit long double emulation.
  32. using boost::multiprecision::cpp_bin_float_quad; // 128-bit quad precision.
  33. //[lambert_w_simple_examples_includes
  34. #include <boost/math/special_functions/lambert_w.hpp> // For lambert_w function.
  35. using boost::math::lambert_w0;
  36. using boost::math::lambert_wm1;
  37. //] //[/lambert_w_simple_examples_includes]
  38. #include <iostream>
  39. // using std::cout;
  40. // using std::endl;
  41. #include <exception>
  42. #include <stdexcept>
  43. #include <string>
  44. #include <limits> // For std::numeric_limits.
  45. //! Show value of z to the full possibly-significant max_digits10 precision of type T.
  46. template<typename T>
  47. void show_value(T z)
  48. {
  49. std::streamsize precision = std::cout.precision(std::numeric_limits<T>::max_digits10); // Save.
  50. std::cout.precision(std::numeric_limits<T>::max_digits10); // Show all posssibly significant digits.
  51. std::ios::fmtflags flags(std::cout.flags());
  52. std::cout.setf(std::ios_base::showpoint); // Include any trailing zeros.
  53. std::cout << z;
  54. // Restore:
  55. std::cout.precision(precision);
  56. std::cout.flags(flags);
  57. } // template<typename T> void show_value(T z)
  58. int main()
  59. {
  60. try
  61. {
  62. std::cout << "Lambert W simple examples." << std::endl;
  63. using boost::math::constants::exp_minus_one; //-1/e, the branch point, a singularity ~= -0.367879.
  64. // using statements needed for changing error handling policy.
  65. using boost::math::policies::policy;
  66. using boost::math::policies::make_policy;
  67. using boost::math::policies::evaluation_error;
  68. using boost::math::policies::domain_error;
  69. using boost::math::policies::overflow_error;
  70. using boost::math::policies::ignore_error;
  71. using boost::math::policies::throw_on_error;
  72. {
  73. //[lambert_w_simple_examples_0
  74. std::cout.precision(std::numeric_limits<double>::max_digits10);
  75. // Show all potentially significant decimal digits,
  76. std::cout << std::showpoint << std::endl;
  77. // and show significant trailing zeros too.
  78. double z = 10.;
  79. double r = lambert_w0(z); // Default policy for double.
  80. std::cout << "lambert_w0(z) = " << r << std::endl;
  81. // lambert_w0(z) = 1.7455280027406994
  82. //] [/lambert_w_simple_examples_0]
  83. }
  84. {
  85. // Other floating-point types can be used too, here float.
  86. // It is convenient to use a function like `show_value`
  87. // to display all potentially significant decimal digits
  88. // for the type, including any significant trailing zeros.
  89. //[lambert_w_simple_examples_1
  90. float z = 10.F;
  91. float r;
  92. r = lambert_w0(z); // Default policy digits10 = 7, digits2 = 24
  93. std::cout << "lambert_w0(";
  94. show_value(z);
  95. std::cout << ") = ";
  96. show_value(r);
  97. std::cout << std::endl; // lambert_w0(10.0000000) = 1.74552798
  98. //] //[/lambert_w_simple_examples_1]
  99. }
  100. {
  101. // Example of an integer argument to lambert_w,
  102. // showing that an integer is correctly promoted to a double.
  103. //[lambert_w_simple_examples_2
  104. std::cout.precision(std::numeric_limits<double>::max_digits10);
  105. double r = lambert_w0(10); // Pass an int argument "10" that should be promoted to double argument.
  106. std::cout << "lambert_w0(10) = " << r << std::endl; // lambert_w0(10) = 1.7455280027406994
  107. double rp = lambert_w0(10);
  108. std::cout << "lambert_w0(10) = " << rp << std::endl;
  109. // lambert_w0(10) = 1.7455280027406994
  110. auto rr = lambert_w0(10); // C++11 needed.
  111. std::cout << "lambert_w0(10) = " << rr << std::endl;
  112. // lambert_w0(10) = 1.7455280027406994 too, showing that rr has been promoted to double.
  113. //] //[/lambert_w_simple_examples_2]
  114. }
  115. {
  116. // Using multiprecision types to get much higher precision is painless.
  117. //[lambert_w_simple_examples_3
  118. cpp_dec_float_50 z("10");
  119. // Note construction using a decimal digit string "10",
  120. // NOT a floating-point double literal 10.
  121. cpp_dec_float_50 r;
  122. r = lambert_w0(z);
  123. std::cout << "lambert_w0("; show_value(z); std::cout << ") = ";
  124. show_value(r);
  125. std::cout << std::endl;
  126. // lambert_w0(10.000000000000000000000000000000000000000000000000000000000000000000000000000000) =
  127. // 1.7455280027406993830743012648753899115352881290809413313533156980404446940000000
  128. //] //[/lambert_w_simple_examples_3]
  129. }
  130. // Using multiprecision types to get multiprecision precision wrong!
  131. {
  132. //[lambert_w_simple_examples_4
  133. cpp_dec_float_50 z(0.7777777777777777777777777777777777777777777777777777777777777777777777777);
  134. // Compiler evaluates the nearest double-precision binary representation,
  135. // from the max_digits10 of the floating_point literal double 0.7777777777777777777777777777...,
  136. // so any extra digits in the multiprecision type
  137. // beyond max_digits10 (usually 17) are random and meaningless.
  138. cpp_dec_float_50 r;
  139. r = lambert_w0(z);
  140. std::cout << "lambert_w0(";
  141. show_value(z);
  142. std::cout << ") = "; show_value(r);
  143. std::cout << std::endl;
  144. // lambert_w0(0.77777777777777779011358916250173933804035186767578125000000000000000000000000000)
  145. // = 0.48086152073210493501934682309060873341910109230469724725005039758139532631901386
  146. //] //[/lambert_w_simple_examples_4]
  147. }
  148. {
  149. //[lambert_w_simple_examples_4a
  150. cpp_dec_float_50 z(0.9); // Construct from floating_point literal double 0.9.
  151. cpp_dec_float_50 r;
  152. r = lambert_w0(0.9);
  153. std::cout << "lambert_w0(";
  154. show_value(z);
  155. std::cout << ") = "; show_value(r);
  156. std::cout << std::endl;
  157. // lambert_w0(0.90000000000000002220446049250313080847263336181640625000000000000000000000000000)
  158. // = 0.52983296563343440510607251781038939952850341796875000000000000000000000000000000
  159. std::cout << "lambert_w0(0.9) = " << lambert_w0(static_cast<double>(0.9))
  160. // lambert_w0(0.9)
  161. // = 0.52983296563343441
  162. << std::endl;
  163. //] //[/lambert_w_simple_examples_4a]
  164. }
  165. {
  166. // Using multiprecision types to get multiprecision precision right!
  167. //[lambert_w_simple_examples_4b
  168. cpp_dec_float_50 z("0.9"); // Construct from decimal digit string.
  169. cpp_dec_float_50 r;
  170. r = lambert_w0(z);
  171. std::cout << "lambert_w0(";
  172. show_value(z);
  173. std::cout << ") = "; show_value(r);
  174. std::cout << std::endl;
  175. // 0.90000000000000000000000000000000000000000000000000000000000000000000000000000000)
  176. // = 0.52983296563343441213336643954546304857788132269804249284012528304239956413801252
  177. //] //[/lambert_w_simple_examples_4b]
  178. }
  179. // Getting extreme precision (1000 decimal digits) Lambert W values.
  180. {
  181. std::cout.precision(std::numeric_limits<cpp_dec_float_1000>::digits10);
  182. cpp_dec_float_1000 z("2.0");
  183. cpp_dec_float_1000 r;
  184. r = lambert_w0(z);
  185. std::cout << "lambert_w0(z) = " << r << std::endl;
  186. // 0.8526055020137254913464724146953174668984533001514035087721073946525150656742630448965773783502494847334503972691804119834761668851953598826198984364998343940330324849743119327028383008883133161249045727544669202220292076639777316648311871183719040610274221013237163543451621208284315007250267190731048119566857455987975973474411544571619699938899354169616378479326962044241495398851839432070255805880208619490399218130868317114428351234208216131218024303904457925834743326836272959669122797896855064630871955955318383064292191644322931561534814178034773896739684452724587331245831001449498844495771266728242975586931792421997636537572767708722190588748148949667744956650966402600446780664924889043543203483210769017254907808218556111831854276511280553252641907484685164978750601216344998778097446525021666473925144772131644151718261199915247932015387685261438125313159125475113124470774926288823525823567568542843625471594347837868505309329628014463491611881381186810879712667681285740515197493390563
  187. // Wolfram alpha command N[productlog[0, 2.0],1000] gives the identical result:
  188. // 0.8526055020137254913464724146953174668984533001514035087721073946525150656742630448965773783502494847334503972691804119834761668851953598826198984364998343940330324849743119327028383008883133161249045727544669202220292076639777316648311871183719040610274221013237163543451621208284315007250267190731048119566857455987975973474411544571619699938899354169616378479326962044241495398851839432070255805880208619490399218130868317114428351234208216131218024303904457925834743326836272959669122797896855064630871955955318383064292191644322931561534814178034773896739684452724587331245831001449498844495771266728242975586931792421997636537572767708722190588748148949667744956650966402600446780664924889043543203483210769017254907808218556111831854276511280553252641907484685164978750601216344998778097446525021666473925144772131644151718261199915247932015387685261438125313159125475113124470774926288823525823567568542843625471594347837868505309329628014463491611881381186810879712667681285740515197493390563
  189. }
  190. {
  191. //[lambert_w_simple_examples_error_policies
  192. // Define an error handling policy:
  193. typedef policy<
  194. domain_error<throw_on_error>,
  195. overflow_error<ignore_error> // possibly unwise?
  196. > my_throw_policy;
  197. std::cout.precision(std::numeric_limits<double>::max_digits10);
  198. // Show all potentially significant decimal digits,
  199. std::cout << std::showpoint << std::endl;
  200. // and show significant trailing zeros too.
  201. double z = +1;
  202. std::cout << "Lambert W (" << z << ") = " << lambert_w0(z) << std::endl;
  203. // Lambert W (1.0000000000000000) = 0.56714329040978384
  204. std::cout << "\nLambert W (" << z << ", my_throw_policy()) = "
  205. << lambert_w0(z, my_throw_policy()) << std::endl;
  206. // Lambert W (1.0000000000000000, my_throw_policy()) = 0.56714329040978384
  207. //] //[/lambert_w_simple_example_error_policies]
  208. }
  209. {
  210. // Show error reporting from passing a value to lambert_wm1 that is out of range,
  211. // (and probably was meant to be passed to lambert_0 instead).
  212. //[lambert_w_simple_examples_out_of_range
  213. double z = +1.;
  214. double r = lambert_wm1(z);
  215. std::cout << "lambert_wm1(+1.) = " << r << std::endl;
  216. //] [/lambert_w_simple_examples_out_of_range]
  217. // Error in function boost::math::lambert_wm1<RealType>(<RealType>):
  218. // Argument z = 1 is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?)
  219. }
  220. }
  221. catch (std::exception& ex)
  222. {
  223. std::cout << ex.what() << std::endl;
  224. }
  225. } // int main()
  226. /*
  227. Output:
  228. //[lambert_w_simple_examples_error_message_1
  229. Error in function boost::math::lambert_wm1<RealType>(<RealType>):
  230. Argument z = 1 is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?)
  231. //] [/lambert_w_simple_examples_error_message_1]
  232. */