static double ErfImp(double z, bool invert) { if (z < 0) { if (!invert) { return(-ErfImp(-z, false)); } if (z < -0.5) { return(2 - ErfImp(-z, true)); } return(1 + ErfImp(-z, false)); } double result; // Big bunch of selection statements now to pick which // implementation to use, try to put most likely options // first: if (z < 0.5) { // We're going to calculate erf: if (z < 1e-10) { result = (z * 1.125) + (z * 0.003379167095512573896158903121545171688); } else { // Worst case absolute error found: 6.688618532e-21 result = (z * 1.125) + (z * Evaluate.Polynomial(z, ErfImpAn) / Evaluate.Polynomial(z, ErfImpAd)); } } else if ((z < 110) || ((z < 110) && invert)) { // We'll be calculating erfc: invert = !invert; double r, b; if (z < 0.75) { // Worst case absolute error found: 5.582813374e-21 r = Evaluate.Polynomial(z - 0.5, ErfImpBn) / Evaluate.Polynomial(z - 0.5, ErfImpBd); b = 0.3440242112F; } else if (z < 1.25) { // Worst case absolute error found: 4.01854729e-21 r = Evaluate.Polynomial(z - 0.75, ErfImpCn) / Evaluate.Polynomial(z - 0.75, ErfImpCd); b = 0.419990927F; } else if (z < 2.25) { // Worst case absolute error found: 2.866005373e-21 r = Evaluate.Polynomial(z - 1.25, ErfImpDn) / Evaluate.Polynomial(z - 1.25, ErfImpDd); b = 0.4898625016F; } else if (z < 3.5) { // Worst case absolute error found: 1.045355789e-21 r = Evaluate.Polynomial(z - 2.25, ErfImpEn) / Evaluate.Polynomial(z - 2.25, ErfImpEd); b = 0.5317370892F; } else if (z < 5.25) { // Worst case absolute error found: 8.300028706e-22 r = Evaluate.Polynomial(z - 3.5, ErfImpFn) / Evaluate.Polynomial(z - 3.5, ErfImpFd); b = 0.5489973426F; } else if (z < 8) { // Worst case absolute error found: 1.700157534e-21 r = Evaluate.Polynomial(z - 5.25, ErfImpGn) / Evaluate.Polynomial(z - 5.25, ErfImpGd); b = 0.5571740866F; } else if (z < 11.5) { // Worst case absolute error found: 3.002278011e-22 r = Evaluate.Polynomial(z - 8, ErfImpHn) / Evaluate.Polynomial(z - 8, ErfImpHd); b = 0.5609807968F; } else if (z < 17) { // Worst case absolute error found: 6.741114695e-21 r = Evaluate.Polynomial(z - 11.5, ErfImpIn) / Evaluate.Polynomial(z - 11.5, ErfImpId); b = 0.5626493692F; } else if (z < 24) { // Worst case absolute error found: 7.802346984e-22 r = Evaluate.Polynomial(z - 17, ErfImpJn) / Evaluate.Polynomial(z - 17, ErfImpJd); b = 0.5634598136F; } else if (z < 38) { // Worst case absolute error found: 2.414228989e-22 r = Evaluate.Polynomial(z - 24, ErfImpKn) / Evaluate.Polynomial(z - 24, ErfImpKd); b = 0.5638477802F; } else if (z < 60) { // Worst case absolute error found: 5.896543869e-24 r = Evaluate.Polynomial(z - 38, ErfImpLn) / Evaluate.Polynomial(z - 38, ErfImpLd); b = 0.5640528202F; } else if (z < 85) { // Worst case absolute error found: 3.080612264e-21 r = Evaluate.Polynomial(z - 60, ErfImpMn) / Evaluate.Polynomial(z - 60, ErfImpMd); b = 0.5641309023F; } else { // Worst case absolute error found: 8.094633491e-22 r = Evaluate.Polynomial(z - 85, ErfImpNn) / Evaluate.Polynomial(z - 85, ErfImpNd); b = 0.5641584396F; } double g = Math.Exp(-z * z) / z; result = (g * b) + (g * r); } else { // Any value of z larger than 28 will underflow to zero: result = 0; invert = !invert; } if (invert) { result = 1 - result; } return(result); }
static double ErfInvImpl(double p, double q, double s) { double result; if (p <= 0.5) { // Evaluate inverse erf using the rational approximation: // // x = p(p+10)(Y+R(p)) // // Where Y is a constant, and R(p) is optimized for a low // absolute error compared to |Y|. // // double: Max error found: 2.001849e-18 // long double: Max error found: 1.017064e-20 // Maximum Deviation Found (actual error term at infinite precision) 8.030e-21 const float y = 0.0891314744949340820313f; double g = p * (p + 10); double r = Evaluate.Polynomial(p, ErvInvImpAn) / Evaluate.Polynomial(p, ErvInvImpAd); result = (g * y) + (g * r); } else if (q >= 0.25) { // Rational approximation for 0.5 > q >= 0.25 // // x = sqrt(-2*log(q)) / (Y + R(q)) // // Where Y is a constant, and R(q) is optimized for a low // absolute error compared to Y. // // double : Max error found: 7.403372e-17 // long double : Max error found: 6.084616e-20 // Maximum Deviation Found (error term) 4.811e-20 const float y = 2.249481201171875f; double g = Math.Sqrt(-2 * Math.Log(q)); double xs = q - 0.25; double r = Evaluate.Polynomial(xs, ErvInvImpBn) / Evaluate.Polynomial(xs, ErvInvImpBd); result = g / (y + r); } else { // For q < 0.25 we have a series of rational approximations all // of the general form: // // let: x = sqrt(-log(q)) // // Then the result is given by: // // x(Y+R(x-B)) // // where Y is a constant, B is the lowest value of x for which // the approximation is valid, and R(x-B) is optimized for a low // absolute error compared to Y. // // Note that almost all code will really go through the first // or maybe second approximation. After than we're dealing with very // small input values indeed: 80 and 128 bit long double's go all the // way down to ~ 1e-5000 so the "tail" is rather long... double x = Math.Sqrt(-Math.Log(q)); if (x < 3) { // Max error found: 1.089051e-20 const float y = 0.807220458984375f; double xs = x - 1.125; double r = Evaluate.Polynomial(xs, ErvInvImpCn) / Evaluate.Polynomial(xs, ErvInvImpCd); result = (y * x) + (r * x); } else if (x < 6) { // Max error found: 8.389174e-21 const float y = 0.93995571136474609375f; double xs = x - 3; double r = Evaluate.Polynomial(xs, ErvInvImpDn) / Evaluate.Polynomial(xs, ErvInvImpDd); result = (y * x) + (r * x); } else if (x < 18) { // Max error found: 1.481312e-19 const float y = 0.98362827301025390625f; double xs = x - 6; double r = Evaluate.Polynomial(xs, ErvInvImpEn) / Evaluate.Polynomial(xs, ErvInvImpEd); result = (y * x) + (r * x); } else if (x < 44) { // Max error found: 5.697761e-20 const float y = 0.99714565277099609375f; double xs = x - 18; double r = Evaluate.Polynomial(xs, ErvInvImpFn) / Evaluate.Polynomial(xs, ErvInvImpFd); result = (y * x) + (r * x); } else { // Max error found: 1.279746e-20 const float y = 0.99941349029541015625f; double xs = x - 44; double r = Evaluate.Polynomial(xs, ErvInvImpGn) / Evaluate.Polynomial(xs, ErvInvImpGd); result = (y * x) + (r * x); } } return(s * result); }