|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe most obvious way of evaluating mathematical functions might not work. For example, how would you calculate This article gives three examples where the most direct evaluation of functions can fail. Along the way, it gives four general rules for accurately evaluating functions. The code included with the article applies the principles in the article to evaluating nine functions that often come up in statistics. Example 1: Quadratic EquationSuppose you want to find the roots of double a = 3.0, b = 1.0e9, c = 5.0;
double d = b*b - 4.0*a*c;
double r1 = (-b - sqrt(d))/(2.0*a);
double r2 = (-b + sqrt(d))/(2.0*a);
This code correctly computes The cardinal rule in numerical analysis is to avoid subtracting nearly equal numbers. The more nearly equal two numbers are, the more precision is lost in the subtraction. Since Rule 1: Use algebraic trickery to avoid loss of precision. There's an equivalent but much less familiar form of the quadratic equation. Multiply the numerator and denominator of the standard form by the numerator with the ± sign turned upside down and simplify. This new form adds double a = 3.0, b = 1.0e9, c = 5.0;
double d = b*b - 4.0*a*c;
double r1 = (-b - sqrt(d))/(2.0*a);
double r2 = -2.0*c/(b + sqrt(d));
There's still some inaccuracy since Along these same lines, see Comparing three methods of computing standard deviation for numerical examples showing how three algebraically equivalent formulas can have very different behavior. A similar phenomena happens when fitting a line to data. Example 2: Ratios of FactorialsProbability calculations often involve taking the ratio of very big numbers to produce a moderate-sized number. The final result may fit inside a There are two ways around this problem. Both use the following rule. Rule 2: Use algebraic trickery to avoid overflow. The first trick is to recognize that 200! = 200*199*198*...*191*190! and so 200!/(190! 10!) = 200*199*198*...*191/10!. This certainly works, but it's limited to factorials. A more general technique is to use logarithms to avoid overflow: take the logarithm of the expression you want to evaluate and then exponentiate the result. In this example, log( 200!/(190! 10!) ) = log(200!) - log(190!) - log(10!). If you have code that calculates the logarithm of factorials directly without calculating factorials first, you could use it to find the logarithm of the result you want, then apply the Example 3: log(1 + x)Now let's look at the example of computing double x = 1e-16;
double y = log(1 + x)/x;
This code sets Double precision numbers are accurate to about 15 decimal places, so This problem is similar to the problem with the quadratic equation, so we might try a similar solution. Unfortunately, algebraic trickery won't help. So we try our second rule. Rule 3: Use analytical approximations to avoid loss of precision. The numerical analysts favorite form of approximation is power series. The power series for Example 4: Inverse LogitNext let's look at computing double x = 1000;
double t = exp(x);
double y = t/(1.0 + t);
Printing out Rule 4: Don't calculate a result you can accurately predict. The header file const double x_max = -log(DBL_EPSILON);
if (x > x_max) return 1.0;
Using the CodeThe code provided with this article calculates seven functions that come up in statistics. Each avoids problems of overflow, underflow, or loss of precision that could occur for large negative arguments, large positive arguments, or arguments near zero.
Points of InterestThe solutions presented here look unnecessary or even wrong at first. If this kind of code isn't well commented, someone will come through and "simplify" it incorrectly. They'll be proud of all the unnecessary clutter they removed. And if they do not test extreme values, their new code will appear to work correctly. The wrong answers and NaNs will only appear later. History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||