private static double erfImp()

in commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/BoostErf.java [133:337]


    private static double erfImp(double z, boolean invert, boolean scaled) {
        if (Double.isNaN(z)) {
            return Double.NaN;
        }

        if (z < 0) {
            // Here the scaled flag is ignored.
            if (!invert) {
                return -erfImp(-z, invert, false);
            } else if (z < -0.5) {
                return 2 - erfImp(-z, invert, false);
            } else {
                return 1 + erfImp(-z, false, false);
            }
        }

        double result;

        //
        // Big bunch of selection statements now to pick
        // which implementation to use,
        // try to put most likely options first:
        //
        if (z < COMPUTE_ERF) {
            //
            // We're going to calculate erf:
            //
            // Here the scaled flag is ignored.
            if (z < 1e-10) {
                if (z == 0) {
                    result = z;
                } else {
                    final double c = 0.003379167095512573896158903121545171688;
                    result = z * 1.125f + z * c;
                }
            } else {
                // Maximum Deviation Found:                      1.561e-17
                // Expected Error Term:                          1.561e-17
                // Maximum Relative Change in Control Points:    1.155e-04
                // Max Error found at double precision =         2.961182e-17

                final double Y = 1.044948577880859375f;
                final double zz = z * z;
                double P;
                P = -0.000322780120964605683831;
                P =  -0.00772758345802133288487 + P * zz;
                P =   -0.0509990735146777432841 + P * zz;
                P =    -0.338165134459360935041 + P * zz;
                P =    0.0834305892146531832907 + P * zz;
                double Q;
                Q = 0.000370900071787748000569;
                Q =  0.00858571925074406212772 + Q * zz;
                Q =   0.0875222600142252549554 + Q * zz;
                Q =    0.455004033050794024546 + Q * zz;
                Q =                        1.0 + Q * zz;
                result = z * (Y + P / Q);
            }
        // Note: Boost threshold of 5.8f has been raised to approximately 5.93 (6073 / 1024);
        // threshold of 28 has been lowered to approximately 27.3 (6989/256) where exp(-z*z) = 0.
        } else if (scaled || (invert ? (z < 27.300781f) : (z < 5.9306640625f))) {
            //
            // We'll be calculating erfc:
            //
            // Here the scaled flag is used.
            invert = !invert;
            if (z < 1.5f) {
                // Maximum Deviation Found:                     3.702e-17
                // Expected Error Term:                         3.702e-17
                // Maximum Relative Change in Control Points:   2.845e-04
                // Max Error found at double precision =        4.841816e-17
                final double Y = 0.405935764312744140625f;
                final double zm = z - 0.5;
                double P;
                P = 0.00180424538297014223957;
                P =  0.0195049001251218801359 + P * zm;
                P =  0.0888900368967884466578 + P * zm;
                P =   0.191003695796775433986 + P * zm;
                P =   0.178114665841120341155 + P * zm;
                P =  -0.098090592216281240205 + P * zm;
                double Q;
                Q = 0.337511472483094676155e-5;
                Q =   0.0113385233577001411017 + Q * zm;
                Q =     0.12385097467900864233 + Q * zm;
                Q =    0.578052804889902404909 + Q * zm;
                Q =     1.42628004845511324508 + Q * zm;
                Q =     1.84759070983002217845 + Q * zm;
                Q =                        1.0 + Q * zm;
                result = Y + P / Q;
                if (scaled) {
                    result /= z;
                } else {
                    result *= expmxx(z) / z;
                }
            } else if (z < 2.5f) {
                // Max Error found at double precision =        6.599585e-18
                // Maximum Deviation Found:                     3.909e-18
                // Expected Error Term:                         3.909e-18
                // Maximum Relative Change in Control Points:   9.886e-05
                final double Y = 0.50672817230224609375f;
                final double zm = z - 1.5;
                double P;
                P = 0.000235839115596880717416;
                P =  0.00323962406290842133584 + P * zm;
                P =   0.0175679436311802092299 + P * zm;
                P =     0.04394818964209516296 + P * zm;
                P =   0.0386540375035707201728 + P * zm;
                P =  -0.0243500476207698441272 + P * zm;
                double Q;
                Q = 0.00410369723978904575884;
                Q =  0.0563921837420478160373 + Q * zm;
                Q =   0.325732924782444448493 + Q * zm;
                Q =   0.982403709157920235114 + Q * zm;
                Q =    1.53991494948552447182 + Q * zm;
                Q =                       1.0 + Q * zm;
                result = Y + P / Q;
                if (scaled) {
                    result /= z;
                } else {
                    result *= expmxx(z) / z;
                }
            // Lowered Boost threshold from 4.5 to 4.0 as this is the limit
            // for the Cody erfc approximation
            } else if (z < 4.0f) {
                // Maximum Deviation Found:                     1.512e-17
                // Expected Error Term:                         1.512e-17
                // Maximum Relative Change in Control Points:   2.222e-04
                // Max Error found at double precision =        2.062515e-17
                final double Y = 0.5405750274658203125f;
                final double zm = z - 3.5;
                double P;
                P = 0.113212406648847561139e-4;
                P = 0.000250269961544794627958 + P * zm;
                P =  0.00212825620914618649141 + P * zm;
                P =  0.00840807615555585383007 + P * zm;
                P =   0.0137384425896355332126 + P * zm;
                P =  0.00295276716530971662634 + P * zm;
                double Q;
                Q = 0.000479411269521714493907;
                Q =   0.0105982906484876531489 + Q * zm;
                Q =   0.0958492726301061423444 + Q * zm;
                Q =    0.442597659481563127003 + Q * zm;
                Q =     1.04217814166938418171 + Q * zm;
                Q =                        1.0 + Q * zm;
                result = Y + P / Q;
                if (scaled) {
                    result /= z;
                } else {
                    result *= expmxx(z) / z;
                }
            } else {
                // Rational function approximation for erfc(x > 4.0)
                //
                // This approximation is not the Boost implementation.
                // The Boost function is suitable for [4.5 < z < 28].
                //
                // This function is suitable for erfcx(z) as it asymptotes
                // to (1 / sqrt(pi)) / z at large z.
                //
                // Taken from "Rational Chebyshev approximations for the error function"
                // by W. J. Cody, Math. Comp., 1969, PP. 631-638.
                //
                // See NUMBERS-177.

                final double izz = 1 / (z * z);
                double p;
                p = 1.63153871373020978498e-2;
                p = 3.05326634961232344035e-1 + p * izz;
                p = 3.60344899949804439429e-1 + p * izz;
                p = 1.25781726111229246204e-1 + p * izz;
                p = 1.60837851487422766278e-2 + p * izz;
                p = 6.58749161529837803157e-4 + p * izz;
                double q;
                q = 1;
                q = 2.56852019228982242072e00 + q * izz;
                q = 1.87295284992346047209e00 + q * izz;
                q = 5.27905102951428412248e-1 + q * izz;
                q = 6.05183413124413191178e-2 + q * izz;
                q = 2.33520497626869185443e-3 + q * izz;

                result = izz * p / q;
                result = (ONE_OVER_ROOT_PI - result) / z;

                if (!scaled) {
                    // exp(-z*z) can be sub-normal so
                    // multiply by any sub-normal after divide by z
                    result *= expmxx(z);
                }
            }
        } else {
            //
            // Any value of z larger than 27.3 will underflow to zero:
            //
            result = 0;
            invert = !invert;
        }

        if (invert) {
            // Note: If 0.5 <= z < 28 and the scaled flag is true then
            // invert will have been flipped to false and the
            // the result is unchanged as erfcx(z)
            result = 1 - result;
        }

        return result;
    }