Here's mine - works to practically useful precision
public class NormalDistribution
{
private static double A1 = -3.969683028665376e+01;
private static double A2 = 2.209460984245205e+02;
private static double A3 = -2.759285104469687e+02;
private static double A4 = 1.383577518672690e+02;
private static double A5 = -3.066479806614716e+01;
private static double A6 = 2.506628277459239e+00;
private static double B1 = -5.447609879822406e+01;
private static double B2 = 1.615858368580409e+02;
private static double B3 = -1.556989798598866e+02;
private static double B4 = 6.680131188771972e+01;
private static double B5 = -1.328068155288572e+01;
private static double C1 = -7.784894002430293e-03;
private static double C2 = -3.223964580411365e-01;
private static double C3 = -2.400758277161838e+00;
private static double C4 = -2.549732539343734e+00;
private static double C5 = 4.374664141464968e+00;
private static double C6 = 2.938163982698783e+00;
private static double D1 = 7.784695709041462e-03;
private static double D2 = 3.224671290700398e-01;
private static double D3 = 2.445134137142996e+00;
private static double D4 = 3.754408661907416e+00;
private static double P_LOW = 0.02425;
// P_high = 1 - p_low
private static double P_HIGH = 0.97575;
/// <summary>
/// Find the Z' such that P = Pr[Z less than Z'];
/// </summary>
/// <param name="P"></param>
public static double InverseNormal(double p)
{
double x = 0;
double q, r;
if ((0 < p) && (p < P_LOW))
{
q = System.Math.Sqrt(-2 * System.Math.Log(p));
x = (((((C1 * q + C2) * q + C3) * q + C4) * q + C5) * q + C6) / ((((D1 * q + D2) * q + D3) * q + D4) * q + 1);
}
else
{
if ((P_LOW <= p) && (p <= P_HIGH))
{
q = p - 0.5;
r = q * q;
x = (((((A1 * r + A2) * r + A3) * r + A4) * r + A5) * r + A6) * q / (((((B1 * r + B2) * r + B3) * r + B4) * r + B5) * r + 1);
}
else
{
if ((P_HIGH < p) && (p < 1))
{
q = System.Math.Sqrt(-2 * System.Math.Log(1 - p));
x = -(((((C1 * q + C2) * q + C3) * q + C4) * q + C5) * q + C6) / ((((D1 * q + D2) * q + D3) * q + D4) * q + 1);
}
}
}
return x;
}
/// <summary>
/// Support function for the bivariate normal
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="aprime"></param>
/// <param name="bprime"></param>
/// <param name="rho"></param>
/// <returns></returns>
private static double f(double x, double y, double aprime, double bprime, double rho)
{
double r = aprime * (2 * x - aprime) + bprime * (2 * y - bprime) + 2 * rho * (x - aprime) * (y - bprime);
return Math.Exp(r);
}
public static double NormPDF(double x)
{
return NormalDistribution.NormPDF(x, 0, 1);
}
public static double NormPDF(double x, double mu, double sigma)
{
double xn = (x - mu) / sigma;
return System.Math.Exp(-0.5 * xn * xn) / (System.Math.Sqrt(2 * System.Math.PI) * sigma);
}
/// <summary>
/// This is the cumulative bivariate normal distribution. Based on
///
http://finance-old.bi.no/~bernt/gcc_prog/recipes/recipes/node23.html
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="rho"></param>
/// <returns></returns>
public static double BivariateNormalCDF(double a, double b, double rho)
{
if ((a <= 0.0) && (b <= 0.0) && (rho <= 0.0))
{
double aprime = a / Math.Sqrt(2.0 * (1.0 - rho * rho));
double bprime = b / Math.Sqrt(2.0 * (1 - rho * rho));
double[] A = new double[4] { 0.3253030, 0.4211071, 0.1334425, 0.006374323 };
double[] B = new double[4] { 0.1337764, 0.6243247, 1.3425378, 2.2626645 };
double sum = 0.0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
sum = sum + A
* A[j] * f(B, B[j], aprime, bprime, rho);
sum = sum * (Math.Sqrt(1.0 - rho * rho) / Math.PI);
return sum;
}
else if (a * b * rho <= 0.0)
{
if ((a <= 0.0) && (b >= 0.0) && (rho >= 0.0))
return NormalCDF(a) - BivariateNormalCDF(a, -1.0 * b, -1.0 * rho);
else if ((a >= 0.0) && (b <= 0.0) && (rho >= 0.0))
return NormalCDF(b) - BivariateNormalCDF(-1.0 * a, b, -1.0 * rho);
else if ((a >= 0.0) && (b >= 0.0) && (rho <= 0.0))
return NormalCDF(a) + NormalCDF(b) - 1.0 + BivariateNormalCDF(-1 * a, -1 * b, rho);
else
throw new Exception("This part of the code should never be reached.");
}
else if (a * b * rho >= 0.0)
{
double denum = Math.Sqrt(a * a - 2 * rho * a * b + b * b);
double rho1 = ((rho * a - b) * Math.Sign(a)) / denum;
double rho2 = ((rho * b - a) * Math.Sign(b)) / denum;
double delta = (1.0 - Math.Sign(a) * Math.Sign(b)) / 4.0;
return BivariateNormalCDF(a, 0.0, rho1) +
BivariateNormalCDF(b, 0.0, rho2) - delta;
}
else
throw new Exception("This part of the code should never be reached.");
}
public static double NormalCDF(double z)
{
if (z > 6.0) return 1;
if (z < -6.0) return 0;
double b1 = 0.31938153;
double b2 = -0.356563782;
double b3 = 1.781477937;
double b4 = -1.821255978;
double b5 = 1.330274429;
double p = 0.2316419;
double c2 = 0.3989423;
double a = System.Math.Abs(z);
double t = 1.0 / (1.0 + a * p);
double b = c2 * System.Math.Exp((-1 * z) * (z / 2.0));
double n = ((((b5 * t + b4) * t + b3) * t + b2) * t + b1) * t;
n = 1.0 - b * n;
if (z < 0.0) n = 1.0 - n;
return n;
}
}