The R Project SVN R

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  R : A Computer Langage for Statistical Data Analysis
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Reference:
 * Cody, W.D. (1993). ALGORITHM 715: SPECFUN - A Portable FORTRAN
 * Package of Special Function Routines and Test Drivers"
 * ACM Transactions on Mathematical Software. 19, 22-32.
 *
 * This function evaluates the normal distribution function:
 * The main computation evaluates near-minimax approximations
 * derived from those in "Rational Chebyshev approximations for
 * the error function" by W. J. Cody, Math. Comp., 1969, 631-637.
 * This transportable program uses rational functions that
 * theoretically approximate the normal distribution function to
 * at least 18 significant decimal digits.  The accuracy achieved
 * depends on the arithmetic system, the compiler, the intrinsic
 * functions, and proper selection of the machine-dependent
 * constants.
 *
 *
 * Mathematical Constants:
 *
 * sqrpi = 1 / sqrt(2*pi),
 * root32 = sqrt(32),
 * thrsh = the argument for which pnorm(thrsh,0,1) = 0.75.
 */

#include "Mathlib.h"

static double c[9] = {
    0.39894151208813466764,
    8.8831497943883759412,
    93.506656132177855979,
    597.27027639480026226,
    2494.5375852903726711,
    6848.1904505362823326,
    11602.651437647350124,
    9842.7148383839780218,
    1.0765576773720192317e-8
};

static double d[8] = {
    22.266688044328115691,
    235.38790178262499861,
    1519.377599407554805,
    6485.558298266760755,
    18615.571640885098091,
    34900.952721145977266,
    38912.003286093271411,
    19685.429676859990727
};

static double p[6] = {
    0.21589853405795699,
    0.1274011611602473639,
    0.022235277870649807,
    0.001421619193227893466,
    2.9112874951168792e-5,
    0.02307344176494017303
};

static double q[5] = {
    1.28426009614491121,
    0.468238212480865118,
    0.0659881378689285515,
    0.00378239633202758244,
    7.29751555083966205e-5
};

static double a[5] = {
    2.2352520354606839287,
    161.02823106855587881,
    1067.6894854603709582,
    18154.981253343561249,
    0.065682337918207449113
};

static double b[4] = {
    47.20258190468824187,
    976.09855173777669322,
    10260.932208618978205,
    45507.789335026729956}
;

static double one = 1.0;
static double half = 0.5;
static double zero = 0.0;
static double sixten = 1.6;
static double sqrpi = 0.39894228040143267794;
static double thrsh = 0.66291;
static double root32 = 5.656854248;

double pnorm(double x, double mean, double sd)
{
    static double xden, temp, xnum, result, ccum;
    static double del, min, eps, xsq;
    static double y;
    static int i;

    if(sd <= 0.0)
        DOMAIN_ERROR;
    x = (x - mean) / sd;
    eps = DBL_EPSILON * .5;
    min = DBL_MIN;
    y = fabs(x);
    if (y <= thrsh) {
        /* Evaluate pnorm for |z| <= 0.66291 */
        xsq = zero;
        if (y > eps) {
            xsq = x * x;
        }
        xnum = a[4] * xsq;
        xden = xsq;
        for (i = 1; i <= 3; ++i) {
            xnum = (xnum + a[i - 1]) * xsq;
            xden = (xden + b[i - 1]) * xsq;
        }
        result = x * (xnum + a[3]) / (xden + b[3]);
        temp = result;
        result = half + temp;
        ccum = half - temp;
    }
    else if (y <= root32) {
        /* Evaluate pnorm for 0.66291 <= |z| <= sqrt(32) */
        xnum = c[8] * y;
        xden = y;
        for (i = 1; i <= 7; ++i) {
            xnum = (xnum + c[i - 1]) * y;
            xden = (xden + d[i - 1]) * y;
        }
        result = (xnum + c[7]) / (xden + d[7]);
        xsq = fint(y * sixten) / sixten;
        del = (y - xsq) * (y + xsq);
        result = exp(-xsq * xsq * half) * exp(-del * half) * result;
        ccum = one - result;
        if (x > zero) {
            temp = result;
            result = ccum;
            ccum = temp;
        }
    }
    else {
        /* Evaluate pnorm for |z| > sqrt(32) */
        result = zero;
        xsq = one / (x * x);
        xnum = p[5] * xsq;
        xden = xsq;
        for (i = 1; i <= 4; ++i) {
            xnum = (xnum + p[i - 1]) * xsq;
            xden = (xden + q[i - 1]) * xsq;
        }
        result = xsq * (xnum + p[4]) / (xden + q[4]);
        result = (sqrpi - result) / y;
        xsq = fint(x * sixten) / sixten;
        del = (x - xsq) * (x + xsq);
        result = exp(-xsq * xsq * half) * exp(-del * half) * result;
        ccum = one - result;
        if (x > zero) {
            temp = result;
            result = ccum;
            ccum = temp;
        }
    }
    if (result < min) {
        result = 0.0;
    }
    if (ccum < min) {
        ccum = 0.0;
    }
    return result;
}