...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
#include <boost/math/special_functions/airy.hpp>
Functions for obtaining both a single zero or root of the Airy functions,
and placing multiple zeros into a container like std::vector
by providing an output iterator.
The signature of the single value functions are:
template <class T> T airy_ai_zero( int m); // 1-based index of zero. template <class T> T airy_bi_zero( int m); // 1-based index of zero.
and for multiple zeros:
template <class T, class OutputIterator> OutputIterator airy_ai_zero( int start_index, // 1-based index of first zero. unsigned number_of_zeros, // How many zeros to generate. OutputIterator out_it); // Destination for zeros. template <class T, class OutputIterator> OutputIterator airy_bi_zero( int start_index, // 1-based index of zero. unsigned number_of_zeros, // How many zeros to generate OutputIterator out_it); // Destination for zeros.
There are also versions which allow control of the Policies for error handling and precision.
template <class T> T airy_ai_zero( int m, // 1-based index of zero. const Policy&); // Policy to use. template <class T> T airy_bi_zero( int m, // 1-based index of zero. const Policy&); // Policy to use. template <class T, class OutputIterator> OutputIterator airy_ai_zero( int start_index, // 1-based index of first zero. unsigned number_of_zeros, // How many zeros to generate. OutputIterator out_it, // Destination for zeros. const Policy& pol); // Policy to use. template <class T, class OutputIterator> OutputIterator airy_bi_zero( int start_index, // 1-based index of zero. unsigned number_of_zeros, // How many zeros to generate. OutputIterator out_it, // Destination for zeros. const Policy& pol); // Policy to use.
The Airy Ai and Bi functions have an infinite number of zeros on the negative real axis. The real zeros on the negative real axis can be found by solving for the roots of
Ai(xm) = 0
Bi(ym) = 0
Here, xm represents the mth root of the Airy Ai function, and ym represents the mth root of the Airy Bi function.
The zeros or roots (values of x
where the function crosses the horizontal y
= 0
axis) of the Airy Ai and Bi functions are computed by two functions, airy_ai_zero
and airy_bi_zero
.
In each case the index or rank of the zero returned is 1-based, which is to say:
airy_ai_zero(1);
returns the first zero of Ai.
Passing an start_index <=
0
results in a domain_error
being raised.
The first few zeros returned by these functions have approximate values as follows:
m |
Ai |
Bi |
---|---|---|
1 |
-2.33811... |
-1.17371... |
2 |
-4.08795... |
-3.27109... |
3 |
-5.52056... |
-4.83074... |
4 |
-6.78671... |
-6.16985... |
5 |
-7.94413... |
-7.37676... |
6 |
-9.02265... |
-8.49195... |
This example demonstrates calculating zeros of the Airy functions. It also shows how Boost.Math and Boost.Multiprecision can be combined to provide a many decimal digit precision. For 50 decimal digit precision we need to include
#include <boost/multiprecision/cpp_dec_float.hpp>
and a typedef
for float_type
may be convenient (allowing
a quick switch to re-compute at built-in double
or other precision)
typedef boost::multiprecision::cpp_dec_float_50 float_type;
To use the functions for finding zeros of the functions we need
#include <boost/math/special_functions/airy.hpp>
This example shows obtaining both a single zero of the Airy functions, and
then placing multiple zeros into a container like std::vector
by providing an iterator. The signature of the single-value Airy Ai function
is:
template <class T> T airy_ai_zero(unsigned m); // 1-based index of the zero.
The signature of multiple zeros Airy Ai function is:
template <class T, class OutputIterator> OutputIterator airy_ai_zero( unsigned start_index, // 1-based index of the zero. unsigned number_of_zeros, // How many zeros to generate. OutputIterator out_it); // Destination for zeros.
There are also versions which allows control of the Policies for error handling and precision.
template <class T, class OutputIterator, class Policy> OutputIterator airy_ai_zero( unsigned start_index, // 1-based index of the zero. unsigned number_of_zeros, // How many zeros to generate. OutputIterator out_it, // Destination for zeros. const Policy& pol); // Policy to use.
Tip | |
---|---|
It is always wise to place code using Boost.Math inside |
First, evaluate a single Airy zero.
The precision is controlled by the template parameter T
,
so this example has double
precision,
at least 15 but up to 17 decimal digits (for the common 64-bit double).
double aiz1 = boost::math::airy_ai_zero<double>(1); std::cout << "boost::math::airy_ai_zero<double>(1) = " << aiz1 << std::endl; double aiz2 = boost::math::airy_ai_zero<double>(2); std::cout << "boost::math::airy_ai_zero<double>(2) = " << aiz2 << std::endl; double biz3 = boost::math::airy_bi_zero<double>(3); std::cout << "boost::math::airy_bi_zero<double>(3) = " << biz3 << std::endl;
Other versions of airy_ai_zero
and airy_bi_zero
allow calculation
of multiple zeros with one call, placing the results in a container, often
std::vector
. For example, generate and display
the first five double
roots
Wolfram
Airy Functions Zeros.
unsigned int n_roots = 5U; std::vector<double> roots; boost::math::airy_ai_zero<double>(1U, n_roots, std::back_inserter(roots)); std::cout << "airy_ai_zeros:" << std::endl; std::copy(roots.begin(), roots.end(), std::ostream_iterator<double>(std::cout, "\n"));
The first few real roots of Ai(x) are approximately -2.33811, -4.08795, -5.52056, -6.7867144, -7.94413, -9.02265 ...
Or we can use Boost.Multiprecision to generate 50 decimal digit roots.
We set the precision of the output stream, and show trailing zeros to display a fixed 50 decimal digits.
std::cout.precision(std::numeric_limits<float_type>::digits10); // float_type has 50 decimal digits. std::cout << std::showpoint << std::endl; // Show trailing zeros too. unsigned int m = 1U; float_type r = boost::math::airy_ai_zero<float_type>(1U); // 1st root. std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ") = " << r << std::endl; m = 2; r = boost::math::airy_ai_zero<float_type>(2U); // 2nd root. std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ") = " << r << std::endl; m = 7U; r = boost::math::airy_bi_zero<float_type>(7U); // 7th root. std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ") = " << r << std::endl; std::vector<float_type> zeros; boost::math::airy_ai_zero<float_type>(1U, 3, std::back_inserter(zeros)); std::cout << "airy_ai_zeros:" << std::endl; // Print the roots to the output stream. std::copy(zeros.begin(), zeros.end(), std::ostream_iterator<float_type>(std::cout, "\n"));
Produces the program output:
boost::math::airy_ai_zero<double>(1) = -2.33811 boost::math::airy_ai_zero<double>(2) = -4.08795 boost::math::airy_bi_zero<double>(3) = -4.83074 airy_ai_zeros: -2.33811 -4.08795 -5.52056 -6.78671 -7.94413 boost::math::airy_bi_zero<float_type>(1) = -2.3381074104597670384891972524467354406385401456711 boost::math::airy_bi_zero<float_type>(2) = -4.0879494441309706166369887014573910602247646991085 boost::math::airy_bi_zero<float_type>(7) = -9.5381943793462388866329885451560196208390720763825 airy_ai_zeros: -2.3381074104597670384891972524467354406385401456711 -4.0879494441309706166369887014573910602247646991085 -5.5205598280955510591298555129312935737972142806175
The full code (and output) for this example is at airy_zeros_example.cpp,
Given the following function (A&S 10.4.105):
Then an initial estimate for the nth zero an of Ai is given by (A&S 10.4.94):
and an initial estimate for the nth zero bn of Bi is given by (A&S 10.4.98):
Thereafter the roots are refined using Newton iteration.
The precision of evaluation of zeros was tested at 50 decimal digits using
cpp_dec_float_50
and found
identical with spot values computed by Wolfram
Alpha.