001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math3.special;
018    
019    import org.apache.commons.math3.util.FastMath;
020    
021    /**
022     * This is a utility class that provides computation methods related to the
023     * error functions.
024     *
025     * @version $Id: Erf.java 1416643 2012-12-03 19:37:14Z tn $
026     */
027    public class Erf {
028    
029        /**
030         * The number {@code X_CRIT} is used by {@link #erf(double, double)} internally.
031         * This number solves {@code erf(x)=0.5} within 1ulp.
032         * More precisely, the current implementations of
033         * {@link #erf(double)} and {@link #erfc(double)} satisfy:<br/>
034         * {@code erf(X_CRIT) < 0.5},<br/>
035         * {@code erf(Math.nextUp(X_CRIT) > 0.5},<br/>
036         * {@code erfc(X_CRIT) = 0.5}, and<br/>
037         * {@code erfc(Math.nextUp(X_CRIT) < 0.5}
038         */
039        private static final double X_CRIT = 0.4769362762044697;
040    
041        /**
042         * Default constructor.  Prohibit instantiation.
043         */
044        private Erf() {}
045    
046        /**
047         * Returns the error function.
048         *
049         * <p>erf(x) = 2/&radic;&pi; <sub>0</sub>&int;<sup>x</sup> e<sup>-t<sup>2</sup></sup>dt </p>
050         *
051         * <p>This implementation computes erf(x) using the
052         * {@link Gamma#regularizedGammaP(double, double, double, int) regularized gamma function},
053         * following <a href="http://mathworld.wolfram.com/Erf.html"> Erf</a>, equation (3)</p>
054         *
055         * <p>The value returned is always between -1 and 1 (inclusive).
056         * If {@code abs(x) > 40}, then {@code erf(x)} is indistinguishable from
057         * either 1 or -1 as a double, so the appropriate extreme value is returned.
058         * </p>
059         *
060         * @param x the value.
061         * @return the error function erf(x)
062         * @throws org.apache.commons.math3.exception.MaxCountExceededException
063         * if the algorithm fails to converge.
064         * @see Gamma#regularizedGammaP(double, double, double, int)
065         */
066        public static double erf(double x) {
067            if (FastMath.abs(x) > 40) {
068                return x > 0 ? 1 : -1;
069            }
070            final double ret = Gamma.regularizedGammaP(0.5, x * x, 1.0e-15, 10000);
071            return x < 0 ? -ret : ret;
072        }
073    
074        /**
075         * Returns the complementary error function.
076         *
077         * <p>erfc(x) = 2/&radic;&pi; <sub>x</sub>&int;<sup>&infin;</sup> e<sup>-t<sup>2</sup></sup>dt
078         * <br/>
079         *    = 1 - {@link #erf(double) erf(x)} </p>
080         *
081         * <p>This implementation computes erfc(x) using the
082         * {@link Gamma#regularizedGammaQ(double, double, double, int) regularized gamma function},
083         * following <a href="http://mathworld.wolfram.com/Erf.html"> Erf</a>, equation (3).</p>
084         *
085         * <p>The value returned is always between 0 and 2 (inclusive).
086         * If {@code abs(x) > 40}, then {@code erf(x)} is indistinguishable from
087         * either 0 or 2 as a double, so the appropriate extreme value is returned.
088         * </p>
089         *
090         * @param x the value
091         * @return the complementary error function erfc(x)
092         * @throws org.apache.commons.math3.exception.MaxCountExceededException
093         * if the algorithm fails to converge.
094         * @see Gamma#regularizedGammaQ(double, double, double, int)
095         * @since 2.2
096         */
097        public static double erfc(double x) {
098            if (FastMath.abs(x) > 40) {
099                return x > 0 ? 0 : 2;
100            }
101            final double ret = Gamma.regularizedGammaQ(0.5, x * x, 1.0e-15, 10000);
102            return x < 0 ? 2 - ret : ret;
103        }
104    
105        /**
106         * Returns the difference between erf(x1) and erf(x2).
107         *
108         * The implementation uses either erf(double) or erfc(double)
109         * depending on which provides the most precise result.
110         *
111         * @param x1 the first value
112         * @param x2 the second value
113         * @return erf(x2) - erf(x1)
114         */
115        public static double erf(double x1, double x2) {
116            if(x1 > x2) {
117                return -erf(x2, x1);
118            }
119    
120            return
121            x1 < -X_CRIT ?
122                x2 < 0.0 ?
123                    erfc(-x2) - erfc(-x1) :
124                    erf(x2) - erf(x1) :
125                x2 > X_CRIT && x1 > 0.0 ?
126                    erfc(x1) - erfc(x2) :
127                    erf(x2) - erf(x1);
128        }
129    }
130