Skip to the content of the web site.

Project B.5: Approximating exponentially distributed events

The standard normal distribution is defined by the probability density function

$\frac{1}{\sqrt{2 \pi}} e^{-\frac{x^2}{2}}$.

This function does not have a simple anti-derivative, so if we wish to compute the integral, we must use the error function ($erf(x)$):

$\int_{-\infty}^x \frac{1}{\sqrt{2 \pi}} e^{-\frac{\xi^2}{2}} d \xi = \frac{1}{2}\left(1 + erf\left(\frac{x}{\sqrt{2}}\right)\right)$.

Fortunately, this function is implemented in the standard mathematics library:

double std_normal_pdf( double x );
double std_normal_cdf( double x );

double std_normal_pdf( double x ) {
	double const PI{std::acos( -1 )};
	return std::exp( -0.5*x*x )/std::sqrt(2*PI);
}

double std_normal_cdf( double x ) {
	return 0.5 + 0.5*std::erf( x/std::sqrt(2.0) );
}

Now, we often must find when the integral of the standard normal equals a specified value. Unfortunately, this has no solution, so we must approximate the solution using Newton's method:

double inv_int_std_normal( double x );

double std_normal_icdf( double x ) {
	assert( (0 < x) && (x < 1) );
	double result{x - 0.5};

	double s{x - 0.5};
	double ss{s*s};

	double x0;
	double x1{s*(2.50662827463100051
               + ss*(2.62493499095373663
               + ss*(5.77253353861173460
               + ss*(15.6676089632851799
               + ss*47.0357874801131660))))};

	do {
		x0 = x1;
		x1 = x1 - std_normal_cdf( x1 )/std_normal_pdf( x1 );
	} while ( std::abs( x0 - x1 ) > 1e-15 );

	return x1;
}