Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Handling functions with large features near an endpoint with tanh-sinh quadrature

Tanh-sinh quadrature has a unique feature which makes it well suited to handling integrals with either singularities or large features of interest near one or both endpoints, it turns out that when we calculate and store the abscissa values at which we will be evaluating the function, we can equally well calculate the difference between the abscissa value and the nearest endpoint. This makes it possible to perform quadrature arbitrarily close to an endpoint, without suffering cancellation error. Note however, that we never actually reach the endpoint, so any endpoint singularity will always be excluded from the quadrature.

The tanh_sinh integration routine will use this additional information internally when performing range transformation, so that for example, if one end of the range is zero (or infinite) then our transformations will get arbitrarily close to the endpoint without precision loss.

However, there are some integrals which may have all of their area near both endpoints, or else near the non-zero endpoint, and in that situation there is a very real risk of loss of precision. For example:

tanh_sinh<double> integrator;
auto f = [](double x) { return sqrt(x / (1 - x * x); };
double Q = integrator.integrate(f, 0.0, 1.0);

Results in very low accuracy as all the area of the integral is near 1, and the 1 - x * x term suffers from cancellation error here.

However, both of tanh_sinh's integration routines will automatically handle 2 argument functors: in this case the first argument is the abscissa value as before, while the second is the distance to the nearest endpoint, ie a - x or b - x if we are integrating over (a,b). You can always differentiate between these 2 cases because the second argument will be negative if we are nearer to the left endpoint.

Knowing this, we can rewrite our lambda expression to take advantage of this additional information:

tanh_sinh<double> integrator;
auto f = [](double x, double xc) { return x <= 0.5 ? sqrt(x) / sqrt(1 - x * x) : sqrt(x / ((x + 1) * (xc))); };
double Q = integrator.integrate(f, 0.0, 1.0);

Not only is this form accurate to full machine-precision, but it converges to the result faster as well.


PrevUpHomeNext