jacobi_zeta_example.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Copyright Paul A. Bristow, 2019
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. /*! \title Simple example of computation of the Jacobi Zeta function using Boost.Math,
  7. and also using corresponding WolframAlpha commands.
  8. */
  9. #ifdef BOOST_NO_CXX11_NUMERIC_LIMITS
  10. # error "This example requires a C++ compiler that supports C++11 numeric_limits. Try C++11 or later."
  11. #endif
  12. #include <boost/math/special_functions/jacobi_zeta.hpp> // For jacobi_zeta function.
  13. #include <boost/multiprecision/cpp_bin_float.hpp> // For cpp_bin_float_50.
  14. #include <iostream>
  15. #include <limits>
  16. #include <iostream>
  17. #include <exception>
  18. int main()
  19. {
  20. try
  21. {
  22. std::cout.precision(std::numeric_limits<double>::max_digits10); // Show all potentially significant digits.
  23. std::cout.setf(std::ios_base::showpoint); // Include any significant trailing zeros.
  24. using boost::math::jacobi_zeta; // jacobi_zeta(T1 k, T2 phi) |k| <=1, k = sqrt(m)
  25. using boost::multiprecision::cpp_bin_float_50;
  26. // Wolfram Mathworld function JacobiZeta[phi, m] where m = k^2
  27. // JacobiZeta[phi,m] gives the Jacobi zeta function Z(phi | m)
  28. // If phi = 2, and elliptic modulus k = 0.9 so m = 0.9 * 0.9 = 0.81
  29. // https://reference.wolfram.com/language/ref/JacobiZeta.html // Function information.
  30. // A simple computation using phi = 2. and m = 0.9 * 0.9
  31. // JacobiZeta[2, 0.9 * 0.9]
  32. // https://www.wolframalpha.com/input/?i=JacobiZeta%5B2,+0.9+*+0.9%5D
  33. // -0.248584...
  34. // To get the expected 17 decimal digits precision for a 64-bit double type,
  35. // we need to ask thus:
  36. // N[JacobiZeta[2, 0.9 * 0.9],17]
  37. // https://www.wolframalpha.com/input/?i=N%5BJacobiZeta%5B2,+0.9+*+0.9%5D,17%5D
  38. double k = 0.9;
  39. double m = k * k;
  40. double phi = 2.;
  41. std::cout << "m = k^2 = " << m << std::endl; // m = k^2 = 0.81000000000000005
  42. std::cout << "jacobi_zeta(" << k << ", " << phi << " ) = " << jacobi_zeta(k, phi) << std::endl;
  43. // jacobi_zeta(0.90000000000000002, 2.0000000000000000 ) =
  44. // -0.24858442708494899 Boost.Math
  45. // -0.24858442708494893 Wolfram
  46. // that agree within the expected precision of 17 decimal digits for 64-bit type double.
  47. // We can also easily get a higher precision too:
  48. // For example, to get 50 decimal digit precision using WolframAlpha:
  49. // N[JacobiZeta[2, 0.9 * 0.9],50]
  50. // https://www.wolframalpha.com/input/?i=N%5BJacobiZeta%5B2,+0.9+*+0.9%5D,50%5D
  51. // -0.24858442708494893408462856109734087389683955309853
  52. // Using Boost.Multiprecision we can do them same almost as easily.
  53. // To check that we are not losing precision, we show all the significant digits of the arguments ad result:
  54. std::cout.precision(std::numeric_limits<cpp_bin_float_50>::digits10); // Show all significant digits.
  55. // We can force the computation to use 50 decimal digit precision thus:
  56. cpp_bin_float_50 k50("0.9");
  57. cpp_bin_float_50 phi50("2.");
  58. std::cout << "jacobi_zeta(" << k50 << ", " << phi50 << " ) = " << jacobi_zeta(k50, phi50) << std::endl;
  59. // jacobi_zeta(0.90000000000000000000000000000000000000000000000000,
  60. // 2.0000000000000000000000000000000000000000000000000 )
  61. // = -0.24858442708494893408462856109734087389683955309853
  62. // and a comparison with Wolfram shows agreement to the expected precision.
  63. // -0.24858442708494893408462856109734087389683955309853 Boost.Math
  64. // -0.24858442708494893408462856109734087389683955309853 Wolfram
  65. // Taking care not to fall into the awaiting pit, we ensure that ALL arguments passed are of the
  66. // appropriate 50-digit precision and do NOT suffer from precision reduction to that of type double,
  67. // We do NOT write:
  68. std::cout << "jacobi_zeta<cpp_bin_float_50>(0.9, 2.) = " << jacobi_zeta<cpp_bin_float_50>(0.9, 2) << std::endl;
  69. // jacobi_zeta(0.90000000000000000000000000000000000000000000000000,
  70. // 2.0000000000000000000000000000000000000000000000000 )
  71. // = -0.24858442708494895921459900494815797085727097762164 << Wrong at about 17th digit!
  72. // -0.24858442708494893408462856109734087389683955309853 Wolfram
  73. }
  74. catch (std::exception const& ex)
  75. {
  76. // Lacking try&catch blocks, the program will abort after any throw, whereas the
  77. // message below from the thrown exception will give some helpful clues as to the cause of the problem.
  78. std::cout << "\n""Message from thrown exception was:\n " << ex.what() << std::endl;
  79. // An example of message:
  80. // std::cout << " = " << jacobi_zeta(2, 0.5) << std::endl;
  81. // Message from thrown exception was:
  82. // Error in function boost::math::ellint_k<long double>(long double) : Got k = 2, function requires |k| <= 1
  83. // Shows that first parameter is k and is out of range, as the definition in docs jacobi_zeta(T1 k, T2 phi);
  84. }
  85. } // int main()