This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git


The following commit(s) were added to refs/heads/master by this push:
     new 99c9ae2  NUMBERS-171: InverseErf to support the full range of [0, 2]
99c9ae2 is described below

commit 99c9ae29ae49634c7c777863f92078a8fad9b41e
Author: Alex Herbert <aherb...@apache.org>
AuthorDate: Thu Oct 14 08:21:15 2021 +0100

    NUMBERS-171: InverseErf to support the full range of [0, 2]
    
    Ported the Boost C++ function boost::math::erfc_inv. This supports
    evaluation from double min value, superceding the support from 2^-53 in
    the previous implementation.
    
    Added a JMH benchmark for the Boost implementation verses the previous
    version.
    
    Documented special case return values for the inverse error functions
    public API.
---
 LICENSE                                            |   7 +
 NOTICE                                             |   6 +
 commons-numbers-examples/examples-jmh/pom.xml      |   5 +
 .../numbers/examples/jmh/gamma/ErfPerformance.java | 720 +++++++++++++++++++++
 .../numbers/examples/jmh/gamma/package-info.java   |  22 +
 commons-numbers-gamma/LICENSE                      |  39 ++
 commons-numbers-gamma/NOTICE                       |   4 +
 .../org/apache/commons/numbers/gamma/BoostErf.java | 372 +++++++++++
 .../apache/commons/numbers/gamma/InverseErf.java   | 109 +---
 .../apache/commons/numbers/gamma/InverseErfc.java  |  20 +-
 .../apache/commons/numbers/gamma/BoostErfTest.java | 433 +++++++++++++
 .../apache/commons/numbers/gamma/TestUtils.java    | 256 ++++++++
 .../commons/numbers/gamma/TestUtilsTest.java       | 168 +++++
 .../apache/commons/numbers/gamma/erf_inv_data.csv  | 122 ++++
 .../commons/numbers/gamma/erf_inv_limit_data.csv   | 129 ++++
 .../commons/numbers/gamma/erfc_inv_big_data.csv    |  71 ++
 .../apache/commons/numbers/gamma/erfc_inv_data.csv | 122 ++++
 .../commons/numbers/gamma/erfc_inv_limit_data.csv  | 126 ++++
 pom.xml                                            |   5 +
 .../checkstyle/checkstyle-suppressions.xml         |   4 +
 src/main/resources/pmd/pmd-ruleset.xml             |  13 +-
 21 files changed, 2644 insertions(+), 109 deletions(-)

diff --git a/LICENSE b/LICENSE
index daa5921..f859d57 100644
--- a/LICENSE
+++ b/LICENSE
@@ -215,6 +215,13 @@ component:
    * Part of Complex is licensed under the Freely Distributable Math
      Library License and the Boost Software License Version 1.0
 
+For the Commons Numbers Gamma (commons-numbers-gamma-x.x.x.jar)
+component:
+
+   * Part of the gamma package is licensed under the
+     Boost Software License Version 1.0
+
+
 Freely Distributable Math Library License
 
 Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
diff --git a/NOTICE b/NOTICE
index baedbfa..8a308cd 100644
--- a/NOTICE
+++ b/NOTICE
@@ -14,3 +14,9 @@ The org.apache.commons.numbers.complex package contains 
derivative
 work originating from the "Boost C++ Libraries" <boost/math/complex>.
 https://www.boost.org/
 Copyright 2005 John Maddock 2005.
+
+For portions of the Commons Numbers Gamma component
+The org.apache.commons.numbers.gamma package contains derivative
+work originating from the "Boost C++ Libraries" <boost/math/special_functions>.
+https://www.boost.org/
+Copyright 2006-7 John Maddock 2006-7.
diff --git a/commons-numbers-examples/examples-jmh/pom.xml 
b/commons-numbers-examples/examples-jmh/pom.xml
index 2e1facc..cc6fe35 100644
--- a/commons-numbers-examples/examples-jmh/pom.xml
+++ b/commons-numbers-examples/examples-jmh/pom.xml
@@ -53,6 +53,11 @@
 
     <dependency>
       <groupId>org.apache.commons</groupId>
+      <artifactId>commons-numbers-gamma</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
       <artifactId>commons-math3</artifactId>
     </dependency>
 
diff --git 
a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/ErfPerformance.java
 
b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/ErfPerformance.java
new file mode 100644
index 0000000..07726c7
--- /dev/null
+++ 
b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/ErfPerformance.java
@@ -0,0 +1,720 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.examples.jmh.gamma;
+
+import java.util.SplittableRandom;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.function.DoubleSupplier;
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.Supplier;
+import java.util.stream.DoubleStream;
+import org.apache.commons.numbers.core.Precision;
+import org.apache.commons.numbers.gamma.Erf;
+import org.apache.commons.numbers.gamma.Erfc;
+import org.apache.commons.numbers.gamma.InverseErf;
+import org.apache.commons.numbers.gamma.InverseErfc;
+import org.apache.commons.numbers.gamma.RegularizedGamma;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+/**
+ * Executes a benchmark to estimate the speed of error function operations.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
+public class ErfPerformance {
+    /** The threshold value for returning the extreme value. */
+    private static final double EXTREME_VALUE_BOUND = 40;
+    /** Commons Numbers 1.0 implementation. */
+    private static final String IMP_NUMBERS_1_0 = "Numbers 1.0";
+    /** Commons Numbers 1.1 implementation. */
+    private static final String IMP_NUMBERS_1_1 = "Boost";
+    /** Uniform numbers in the appropriate domain of the function. */
+    private static final String NUM_UNIFORM = "uniform";
+    /** Uniform numbers in the domain of the error function result, [1, 1] or 
[0, 2]. */
+    private static final String NUM_INVERSE_UNIFORM = "inverse uniform";
+    /** Message prefix for an unknown parameter. */
+    private static final String UNKNOWN = "unknown parameter: ";
+    /** Message prefix for a erf domain error. */
+    private static final String ERF_DOMAIN_ERROR = "erf domain error: ";
+    /** Message prefix for a erf domain error. */
+    private static final String ERFC_DOMAIN_ERROR = "erfc domain error: ";
+
+    /**
+     * The seed for random number generation. Ensures the same numbers are 
generated
+     * for each implementation of the function.
+     *
+     * <p>Note: Numbers will not be the same for the error function and 
complementary error
+     * function. The domain is shifted from [-1, 1] to [0, 2]. However number 
generation
+     * may wish to target specific regions of the domain where there is 
limited precision
+     * as {@code |x| -> 1} or high precision as {@code |x| -> 0}.
+     */
+    private static final long SEED = ThreadLocalRandom.current().nextLong();
+
+    /**
+     * Contains an array of numbers.
+     */
+    public abstract static class NumberData {
+        /** The size of the data. */
+        @Param({"1000"})
+        private int size;
+
+        /** The numbers. */
+        private double[] numbers;
+
+        /**
+         * Gets the size of the array of numbers.
+         *
+         * @return the size
+         */
+        public int getSize() {
+            return size;
+        }
+
+        /**
+         * Gets the numbers.
+         *
+         * @return the numbers
+         */
+        public double[] getNumbers() {
+            return numbers;
+        }
+
+        /**
+         * Create the numbers.
+         */
+        @Setup
+        public void setup() {
+            numbers = createNumbers(new SplittableRandom(SEED));
+        }
+
+        /**
+         * Creates the numbers.
+         *
+         * @param rng Random number generator.
+         * @return the random numbers
+         * @see #getSize()
+         */
+        protected abstract double[] createNumbers(SplittableRandom rng);
+    }
+
+    /**
+     * Contains an array of numbers. This is used to test the JMH overhead for 
calling
+     * a function on each number in the array.
+     */
+    @State(Scope.Benchmark)
+    public static class BaseData extends NumberData {
+        /** {@inheritDoc} */
+        @Override
+        protected double[] createNumbers(SplittableRandom rng) {
+            return rng.doubles().limit(getSize()).toArray();
+        }
+    }
+
+    /**
+     * Contains an array of numbers and the method to compute the error 
function.
+     */
+    public abstract static class FunctionData extends NumberData {
+
+        /** The function. */
+        private DoubleUnaryOperator function;
+
+        /**
+         * The implementation of the function.
+         */
+        @Param({IMP_NUMBERS_1_0, IMP_NUMBERS_1_1})
+        private String implementation;
+
+        /**
+         * Gets the implementation.
+         *
+         * @return the implementation
+         */
+        public String getImplementation() {
+            return implementation;
+        }
+
+        /**
+         * Gets the function.
+         *
+         * @return the function
+         */
+        public DoubleUnaryOperator getFunction() {
+            return function;
+        }
+
+        /**
+         * Create the numbers and the function.
+         */
+        @Override
+        @Setup
+        public void setup() {
+            super.setup();
+            function = createFunction();
+            verify();
+        }
+
+        /**
+         * Creates the function from the implementation name.
+         *
+         * @return the inverse error function
+         * @see #getImplementation()
+         */
+        protected abstract DoubleUnaryOperator createFunction();
+
+        /**
+         * Verify the numbers for the function. This is called after the 
numbers and function
+         * have been created.
+         *
+         * @see #getNumbers()
+         * @see #getFunction()
+         */
+        protected abstract void verify();
+    }
+
+    /**
+     * Contains an array of numbers in the range for the error function.
+     */
+    @State(Scope.Benchmark)
+    public static class ErfData extends FunctionData {
+        /** The type of the data. */
+        @Param({NUM_UNIFORM, NUM_INVERSE_UNIFORM})
+        private String type;
+
+        /** {@inheritDoc} */
+        @Override
+        protected double[] createNumbers(SplittableRandom rng) {
+            DoubleSupplier generator;
+            if (NUM_INVERSE_UNIFORM.equals(type)) {
+                // p range: [-1, 1)
+                // The final value is generated using the inverse erf function.
+                generator = () -> InverseErf.value(makeSignedDouble(rng));
+            } else if (NUM_UNIFORM.equals(type)) {
+                // range [-6, 6)
+                // Note: Values are not distinguishable from +/-1 when |x| > 6
+                generator = () -> makeSignedDouble(rng) * 6;
+            } else {
+                throw new IllegalStateException(UNKNOWN + type);
+            }
+            return DoubleStream.generate(generator).limit(getSize()).toArray();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected DoubleUnaryOperator createFunction() {
+            final String impl = getImplementation();
+            if (IMP_NUMBERS_1_0.equals(impl)) {
+                return ErfPerformance::erf;
+            } else if (IMP_NUMBERS_1_1.equals(impl)) {
+                return Erf::value;
+            } else {
+                throw new IllegalStateException(UNKNOWN + impl);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected void verify() {
+            final DoubleUnaryOperator function = getFunction();
+            final double relativeEps = 1e-6;
+            for (final double x : getNumbers()) {
+                final double p = function.applyAsDouble(x);
+                assert -1 <= p & p <= 1 : ERF_DOMAIN_ERROR + p;
+
+                // Implementations may not compute a round-trip
+                // to a suitable accuracy as:
+                // |p| -> 0 : x -> 0
+                // |p| -> 1 : x -> +/-big
+                if (p < 1e-10 || Math.abs(p - 1) < 1e-10) {
+                    continue;
+                }
+                assertEquals(x, InverseErf.value(p), Math.abs(x) * relativeEps,
+                    () -> getImplementation() + " inverse erf " + p);
+            }
+        }
+    }
+
+    /**
+     * Contains an array of numbers in the range for the complementary error 
function.
+     */
+    @State(Scope.Benchmark)
+    public static class ErfcData extends FunctionData {
+        /** The type of the data. */
+        @Param({NUM_UNIFORM, NUM_INVERSE_UNIFORM})
+        private String type;
+
+        /** {@inheritDoc} */
+        @Override
+        protected double[] createNumbers(SplittableRandom rng) {
+            DoubleSupplier generator;
+            if (NUM_INVERSE_UNIFORM.equals(type)) {
+                // q range: [0, 2)
+                // The final value is generated using the inverse erfc 
function.
+                generator = () -> InverseErfc.value(rng.nextDouble() * 2);
+            } else if (NUM_UNIFORM.equals(type)) {
+                // range [-6, 28)
+                // Note: Values are not distinguishable from 2 when x < -6
+                // Shift the range [-17, 17) to [-6, 28)
+                generator = () -> makeSignedDouble(rng) * 17 + 11;
+            } else {
+                throw new IllegalStateException(UNKNOWN + type);
+            }
+            return DoubleStream.generate(generator).limit(getSize()).toArray();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected DoubleUnaryOperator createFunction() {
+            final String impl = getImplementation();
+            if (IMP_NUMBERS_1_0.equals(impl)) {
+                return ErfPerformance::erfc;
+            } else if (IMP_NUMBERS_1_1.equals(impl)) {
+                return Erfc::value;
+            } else {
+                throw new IllegalStateException(UNKNOWN + impl);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected void verify() {
+            final DoubleUnaryOperator function = getFunction();
+            final double relativeEps = 1e-6;
+            for (final double x : getNumbers()) {
+                final double q = function.applyAsDouble(x);
+                assert 0 <= q && q <= 2 : ERFC_DOMAIN_ERROR + q;
+
+                // Implementations may not compute a round-trip
+                // to a suitable accuracy as:
+                // q -> 0 : x -> big
+                // |q| -> 1 : x -> 0
+                // q -> 2 : x -> -big
+                if (q < 1e-10 || Math.abs(q - 1) < 1e-10 || q > 2 - 1e-10) {
+                    continue;
+                }
+                assertEquals(x, InverseErfc.value(q), Math.abs(x) * 
relativeEps,
+                    () -> getImplementation() + " inverse erfc " + q);
+            }
+        }
+    }
+
+    /**
+     * Contains an array of numbers in the range [-1, 1] for the inverse error 
function.
+     */
+    @State(Scope.Benchmark)
+    public static class InverseErfData extends FunctionData {
+        /**
+         * The type of the data.
+         */
+        @Param({NUM_UNIFORM})
+        private String type;
+
+        /** {@inheritDoc} */
+        @Override
+        protected double[] createNumbers(SplittableRandom rng) {
+            DoubleSupplier generator;
+            if (NUM_UNIFORM.equals(type)) {
+                // range [-1, 1)
+                generator = () -> makeSignedDouble(rng);
+            } else {
+                throw new IllegalStateException(UNKNOWN + type);
+            }
+            return DoubleStream.generate(generator).limit(getSize()).toArray();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected DoubleUnaryOperator createFunction() {
+            final String impl = getImplementation();
+            if (IMP_NUMBERS_1_0.equals(impl)) {
+                return ErfPerformance::inverseErf;
+            } else if (IMP_NUMBERS_1_1.equals(impl)) {
+                return InverseErf::value;
+            } else {
+                throw new IllegalStateException(UNKNOWN + impl);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected void verify() {
+            final DoubleUnaryOperator function = getFunction();
+            final double relativeEps = 1e-12;
+            for (final double x : getNumbers()) {
+                assert -1 <= x && x <= 1 : ERF_DOMAIN_ERROR + x;
+
+                // Implementations may not compute a round-trip
+                // to a suitable accuracy as:
+                // |x| -> 0 : t -> 0
+                // |x| -> 1 : t -> +/-big
+                if (x < 1e-10 || Math.abs(x - 1) < 1e-10) {
+                    continue;
+                }
+                final double t = function.applyAsDouble(x);
+                assertEquals(x, Erf.value(t), Math.abs(x) * relativeEps,
+                    () -> getImplementation() + " erf " + t);
+            }
+        }
+    }
+
+    /**
+     * Contains an array of numbers in the range [0, 2] for the inverse 
complementary error function.
+     */
+    @State(Scope.Benchmark)
+    public static class InverseErfcData extends FunctionData {
+        /**
+         * The type of the data.
+         */
+        @Param({NUM_UNIFORM})
+        private String type;
+
+        /** {@inheritDoc} */
+        @Override
+        protected double[] createNumbers(SplittableRandom rng) {
+            DoubleSupplier generator;
+            if (NUM_UNIFORM.equals(type)) {
+                // range [0, 2)
+                generator = () -> rng.nextDouble() * 2;
+            } else {
+                throw new IllegalStateException(UNKNOWN + type);
+            }
+            return DoubleStream.generate(generator).limit(getSize()).toArray();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected DoubleUnaryOperator createFunction() {
+            final String impl = getImplementation();
+            if (IMP_NUMBERS_1_0.equals(impl)) {
+                return ErfPerformance::inverseErfc;
+            } else if (IMP_NUMBERS_1_1.equals(impl)) {
+                return InverseErfc::value;
+            } else {
+                throw new IllegalStateException(UNKNOWN + impl);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected void verify() {
+            final DoubleUnaryOperator function = getFunction();
+            final double relativeEps = 1e-12;
+            for (final double x : getNumbers()) {
+                assert 0 <= x && x <= 2 : ERFC_DOMAIN_ERROR + x;
+
+                // Implementations may not compute a round-trip
+                // to a suitable accuracy as:
+                // x -> 0 : t -> big
+                // |x| -> 1 : t -> 0
+                // x -> 2 : t -> -big
+                if (x < 1e-10 || Math.abs(x - 1) < 1e-10 || x > 2 - 1e-10) {
+                    continue;
+                }
+                final double t = function.applyAsDouble(x);
+                assertEquals(x, Erfc.value(t), Math.abs(x) * relativeEps,
+                    () -> getImplementation() + " erfc " + t);
+            }
+        }
+    }
+
+    /**
+     * Make a signed double in the range [-1, 1).
+     *
+     * @param rng Random generator
+     * @return u in [-1, 1)
+     */
+    private static double makeSignedDouble(SplittableRandom rng) {
+        // As per o.a.c.rng.core.utils.NumberFactory.makeDouble(long) but 
using a signed
+        // shift of 10 in place of an unsigned shift of 11.
+        // Use the upper 54 bits on the assumption they are more random.
+        // The sign bit is maintained by the signed shift.
+        // The next 53 bits generates a magnitude in the range [0, 2^53) or 
[-2^53, 0).
+        return (rng.nextLong() >> 10) * 0x1.0p-53;
+    }
+
+    /**
+     * Returns the inverse complementary error function.
+     *
+     * <p>This is the implementation in Commons Numbers 1.0.
+     *
+     * @param x Value in [0, 2].
+     * @return t such that {@code x = erfc(t)}
+     */
+    private static double inverseErfc(final double x) {
+        return inverseErf(1 - x);
+    }
+
+    /**
+     * Returns the inverse error function.
+     *
+     * <p>This implementation is described in the paper:
+     * <a 
href="http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf";>Approximating
+     * the erfinv function</a> by Mike Giles, Oxford-Man Institute of 
Quantitative Finance,
+     * which was published in GPU Computing Gems, volume 2, 2010.
+     * The source code is available <a 
href="http://gpucomputing.net/?q=node/1828";>here</a>.
+     * </p>
+     *
+     * <p>This is the implementation in Commons Numbers 1.0.
+     *
+     * @param x Value in [-1, 1].
+     * @return t such that {@code x = erf(t)}
+     */
+    private static double inverseErf(final double x) {
+        // Beware that the logarithm argument must be
+        // computed as (1 - x) * (1 + x),
+        // it must NOT be simplified as 1 - x * x as this
+        // would induce rounding errors near the boundaries +/-1
+        double w = -Math.log((1 - x) * (1 + x));
+        double p;
+
+        if (w < 6.25) {
+            w -= 3.125;
+            p =  -3.6444120640178196996e-21;
+            p =   -1.685059138182016589e-19 + p * w;
+            p =   1.2858480715256400167e-18 + p * w;
+            p =    1.115787767802518096e-17 + p * w;
+            p =   -1.333171662854620906e-16 + p * w;
+            p =   2.0972767875968561637e-17 + p * w;
+            p =   6.6376381343583238325e-15 + p * w;
+            p =  -4.0545662729752068639e-14 + p * w;
+            p =  -8.1519341976054721522e-14 + p * w;
+            p =   2.6335093153082322977e-12 + p * w;
+            p =  -1.2975133253453532498e-11 + p * w;
+            p =  -5.4154120542946279317e-11 + p * w;
+            p =    1.051212273321532285e-09 + p * w;
+            p =  -4.1126339803469836976e-09 + p * w;
+            p =  -2.9070369957882005086e-08 + p * w;
+            p =   4.2347877827932403518e-07 + p * w;
+            p =  -1.3654692000834678645e-06 + p * w;
+            p =  -1.3882523362786468719e-05 + p * w;
+            p =    0.0001867342080340571352 + p * w;
+            p =  -0.00074070253416626697512 + p * w;
+            p =   -0.0060336708714301490533 + p * w;
+            p =      0.24015818242558961693 + p * w;
+            p =       1.6536545626831027356 + p * w;
+        } else if (w < 16.0) {
+            w = Math.sqrt(w) - 3.25;
+            p =   2.2137376921775787049e-09;
+            p =   9.0756561938885390979e-08 + p * w;
+            p =  -2.7517406297064545428e-07 + p * w;
+            p =   1.8239629214389227755e-08 + p * w;
+            p =   1.5027403968909827627e-06 + p * w;
+            p =   -4.013867526981545969e-06 + p * w;
+            p =   2.9234449089955446044e-06 + p * w;
+            p =   1.2475304481671778723e-05 + p * w;
+            p =  -4.7318229009055733981e-05 + p * w;
+            p =   6.8284851459573175448e-05 + p * w;
+            p =   2.4031110387097893999e-05 + p * w;
+            p =   -0.0003550375203628474796 + p * w;
+            p =   0.00095328937973738049703 + p * w;
+            p =   -0.0016882755560235047313 + p * w;
+            p =    0.0024914420961078508066 + p * w;
+            p =   -0.0037512085075692412107 + p * w;
+            p =     0.005370914553590063617 + p * w;
+            p =       1.0052589676941592334 + p * w;
+            p =       3.0838856104922207635 + p * w;
+        } else if (w < Double.POSITIVE_INFINITY) {
+            w = Math.sqrt(w) - 5;
+            p =  -2.7109920616438573243e-11;
+            p =  -2.5556418169965252055e-10 + p * w;
+            p =   1.5076572693500548083e-09 + p * w;
+            p =  -3.7894654401267369937e-09 + p * w;
+            p =   7.6157012080783393804e-09 + p * w;
+            p =  -1.4960026627149240478e-08 + p * w;
+            p =   2.9147953450901080826e-08 + p * w;
+            p =  -6.7711997758452339498e-08 + p * w;
+            p =   2.2900482228026654717e-07 + p * w;
+            p =  -9.9298272942317002539e-07 + p * w;
+            p =   4.5260625972231537039e-06 + p * w;
+            p =  -1.9681778105531670567e-05 + p * w;
+            p =   7.5995277030017761139e-05 + p * w;
+            p =  -0.00021503011930044477347 + p * w;
+            p =  -0.00013871931833623122026 + p * w;
+            p =       1.0103004648645343977 + p * w;
+            p =       4.8499064014085844221 + p * w;
+        } else if (w == Double.POSITIVE_INFINITY) {
+            // this branch does not appears in the original code, it
+            // was added because the previous branch does not handle
+            // x = +/-1 correctly. In this case, w is positive infinity
+            // and as the first coefficient (-2.71e-11) is negative.
+            // Once the first multiplication is done, p becomes negative
+            // infinity and remains so throughout the polynomial evaluation.
+            // So the branch above incorrectly returns negative infinity
+            // instead of the correct positive infinity.
+            p = Double.POSITIVE_INFINITY;
+        } else {
+            // this branch does not appears in the original code, it
+            // occurs when the input is NaN or not in the range [-1, 1].
+            return Double.NaN;
+        }
+
+        return p * x;
+    }
+
+    /**
+     * Returns the complementary error function.
+     *
+     * <p>This implementation computes erfc(x) using the
+     * {@link RegularizedGamma.Q#value(double, double, double, int) 
regularized gamma function},
+     * following <a href="http://mathworld.wolfram.com/Erf.html";>Erf</a>, 
equation (3).
+     *
+     * <p>This is the implementation in Commons Numbers 1.0.
+     *
+     * @param x Value in [0, 2].
+     * @return t such that {@code x = erfc(t)}
+     */
+    private static double erfc(final double x) {
+        if (Math.abs(x) > EXTREME_VALUE_BOUND) {
+            return x > 0 ? 0 : 2;
+        }
+        final double ret = RegularizedGamma.Q.value(0.5, x * x, 1e-15, 10000);
+        return x < 0 ? 2 - ret : ret;
+    }
+
+    /**
+     * Returns the error function.
+     *
+     * <p>This implementation computes erf(x) using the
+     * {@link RegularizedGamma.P#value(double, double, double, int) 
regularized gamma function},
+     * following <a href="http://mathworld.wolfram.com/Erf.html";> Erf</a>, 
equation (3)
+     *
+     * <p>This is the implementation in Commons Numbers 1.0.
+     *
+     * @param x Value in [-1, 1].
+     * @return t such that {@code x = erf(t)}
+     */
+    private static double erf(final double x) {
+        if (Math.abs(x) > EXTREME_VALUE_BOUND) {
+            return x > 0 ? 1 : -1;
+        }
+        final double ret = RegularizedGamma.P.value(0.5, x * x, 1e-15, 10000);
+        return x < 0 ? -ret : ret;
+    }
+
+    /**
+     * Assert the values are equal to the given epsilon, else throw an 
AssertionError.
+     *
+     * @param x the x
+     * @param y the y
+     * @param eps the max epsilon for equality
+     * @param msg the message upon failure
+     */
+    static void assertEquals(double x, double y, double eps, Supplier<String> 
msg) {
+        if (!Precision.equalsIncludingNaN(x, y, eps)) {
+            throw new AssertionError(msg.get() + ": " + x + " != " + y);
+        }
+    }
+
+    /**
+     * Apply the function to all the numbers.
+     *
+     * @param numbers Numbers.
+     * @param fun Function.
+     * @param bh Data sink.
+     */
+    private static void apply(double[] numbers, DoubleUnaryOperator fun, 
Blackhole bh) {
+        for (int i = 0; i < numbers.length; i++) {
+            bh.consume(fun.applyAsDouble(numbers[i]));
+        }
+    }
+
+    /**
+     * Identity function. This can be used to measure the JMH overhead of 
calling a function
+     * on an array of numbers.
+     *
+     * @param z Number.
+     * @return the number
+     */
+    private static double identity(double z) {
+        return z;
+    }
+
+    // Benchmark methods.
+    // Benchmarks use function references to perform different operations on 
the numbers.
+
+    /**
+     * Baseline the JMH overhead for all the benchmarks that evaluate a 
function of
+     * an array of numbers. All other methods are expected to be slower than 
this.
+     *
+     * @param numbers Numbers.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void baseline(BaseData numbers, Blackhole bh) {
+        apply(numbers.getNumbers(), ErfPerformance::identity, bh);
+    }
+
+    /**
+     * Benchmark the error function.
+     *
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void erf(ErfData data, Blackhole bh) {
+        apply(data.getNumbers(), data.getFunction(), bh);
+    }
+
+    /**
+     * Benchmark the complementary error function.
+     *
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void erfc(ErfcData data, Blackhole bh) {
+        apply(data.getNumbers(), data.getFunction(), bh);
+    }
+
+    /**
+     * Benchmark the inverse error function.
+     *
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void inverseErf(InverseErfData data, Blackhole bh) {
+        apply(data.getNumbers(), data.getFunction(), bh);
+    }
+
+    /**
+     * Benchmark the inverse complementary error function.
+     *
+     * @param data Test data.
+     * @param bh Data sink.
+     */
+    @Benchmark
+    public void inverseErfc(InverseErfcData data, Blackhole bh) {
+        apply(data.getNumbers(), data.getFunction(), bh);
+    }
+}
diff --git 
a/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/package-info.java
 
b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/package-info.java
new file mode 100644
index 0000000..57911bd
--- /dev/null
+++ 
b/commons-numbers-examples/examples-jmh/src/main/java/org/apache/commons/numbers/examples/jmh/gamma/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Benchmarks for the {@code org.apache.commons.numbers.gamma} components.
+ */
+
+package org.apache.commons.numbers.examples.jmh.gamma;
diff --git a/commons-numbers-gamma/LICENSE b/commons-numbers-gamma/LICENSE
index 261eeb9..d8ef8fb 100644
--- a/commons-numbers-gamma/LICENSE
+++ b/commons-numbers-gamma/LICENSE
@@ -199,3 +199,42 @@
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
+
+
+
+APACHE COMMONS NUMBERS SUBCOMPONENTS:
+
+Apache Commons Numbers includes a number of subcomponents with separate
+copyright notices and license terms. Your use of these subcomponents is
+subject to the terms and conditions of the following licenses.
+
+
+For the Commons Numbers Gamma (commons-numbers-gamma-x.x.x.jar)
+component:
+
+   * Part of the gamma package is licensed under the
+     Boost Software License Version 1.0
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/commons-numbers-gamma/NOTICE b/commons-numbers-gamma/NOTICE
index c1a3017..5bbc4dd 100644
--- a/commons-numbers-gamma/NOTICE
+++ b/commons-numbers-gamma/NOTICE
@@ -4,3 +4,7 @@ Copyright 2001-2021 The Apache Software Foundation
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 
+The org.apache.commons.numbers.gamma package contains derivative
+work originating from the "Boost C++ Libraries" <boost/math/special_functions>.
+https://www.boost.org/
+Copyright 2006-7 John Maddock 2006-7.
diff --git 
a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/BoostErf.java
 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/BoostErf.java
new file mode 100644
index 0000000..08845fc
--- /dev/null
+++ 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/BoostErf.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//  (C) Copyright John Maddock 2006.
+//  Use, modification and distribution are subject to the
+//  Boost Software License, Version 1.0. (See accompanying file
+//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+package org.apache.commons.numbers.gamma;
+
+/**
+ * Implementation of the inverse of the <a 
href="http://mathworld.wolfram.com/Erf.html";>error function</a>.
+ *
+ * <p>This code has been adapted from the <a 
href="https://www.boost.org/";>Boost</a>
+ * {@code c++} implementation {@code <boost/math/cspecial_functions/erf.hpp>}.
+ * All work is copyright John Maddock 2006 and subject to the Boost Software 
License.
+ *
+ * @see
+ * <a 
href="https://www.boost.org/doc/libs/1_77_0/libs/math/doc/html/math_toolkit/sf_erf/error_function.html";>
+ * Boost C++ Error functions</a>
+ */
+final class BoostErf {
+    /** Private constructor. */
+    private BoostErf() {
+        // intentionally empty.
+    }
+
+    // Code ported from Boost 1.77.0
+    //
+    // boost/math/special_functions/erf.hpp
+    // boost/math/special_functions/detail/erf_inv.hpp
+    //
+    // Original code comments, including measured deviations, are preserved.
+    //
+    // Changes to the Boost implementation:
+    // - Update method names to replace underscores with camel case
+    // - Explicitly inline the polynomial function evaluation
+    //   using Horner's method 
(https://en.wikipedia.org/wiki/Horner%27s_method)
+    // - Support odd function for f(0.0) = -f(-0.0)
+    // Inverse erf:
+    // - Change inverse erf edge case detection to include NaN
+    //
+    // Note:
+    // Constants using the 'f' suffix are machine
+    // representable as a float, e.g.
+    // assert 0.0891314744949340820313f == 0.0891314744949340820313;
+    // The values are unchanged from the Boost reference.
+
+    /**
+     * Returns the inverse complementary error function.
+     *
+     * @param z Value (in {@code [0, 2]}).
+     * @return t such that {@code z = erfc(t)}
+     */
+    static double erfcInv(double z) {
+        //
+        // Begin by testing for domain errors, and other special cases:
+        //
+        if (z < 0 || z > 2 || Double.isNaN(z)) {
+            // Argument outside range [0,2] in inverse erfc function
+            return Double.NaN;
+        }
+        // Domain bounds must be detected as the implementation computes NaN.
+        // (log(q=0) creates infinity and the rational number is
+        // infinity / infinity)
+        if (z == (int) z) {
+            // z   return
+            // 2   -inf
+            // 1   0
+            // 0   inf
+            return z == 1 ? 0 : (1 - z) * Double.POSITIVE_INFINITY;
+        }
+
+        //
+        // Normalise the input, so it's in the range [0,1], we will
+        // negate the result if z is outside that range. This is a simple
+        // application of the erfc reflection formula: erfc(-z) = 2 - erfc(z)
+        //
+        double p;
+        double q;
+        double s;
+        if (z > 1) {
+            q = 2 - z;
+            p = 1 - q;
+            s = -1;
+        } else {
+            p = 1 - z;
+            q = z;
+            s = 1;
+        }
+
+        //
+        // And get the result, negating where required:
+        //
+        return s * erfInvImp(p, q);
+    }
+
+    /**
+     * Returns the inverse error function.
+     *
+     * @param z Value (in {@code [-1, 1]}).
+     * @return t such that {@code z = erf(t)}
+     */
+    static double erfInv(double z) {
+        //
+        // Begin by testing for domain errors, and other special cases:
+        //
+        if (z < -1 || z > 1 || Double.isNaN(z)) {
+            // Argument outside range [-1, 1] in inverse erf function
+            return Double.NaN;
+        }
+        // Domain bounds must be detected as the implementation computes NaN.
+        // (log(q=0) creates infinity and the rational number is
+        // infinity / infinity)
+        if (z == (int) z) {
+            // z   return
+            // -1  -inf
+            // -0  -0
+            // 0   0
+            // 1   inf
+            return z == 0 ? z : z * Double.POSITIVE_INFINITY;
+        }
+
+        //
+        // Normalise the input, so it's in the range [0,1], we will
+        // negate the result if z is outside that range. This is a simple
+        // application of the erf reflection formula: erf(-z) = -erf(z)
+        //
+        double p;
+        double q;
+        double s;
+        if (z < 0) {
+            p = -z;
+            q = 1 - p;
+            s = -1;
+        } else {
+            p = z;
+            q = 1 - z;
+            s = 1;
+        }
+        //
+        // And get the result, negating where required:
+        //
+        return s * erfInvImp(p, q);
+    }
+
+    /**
+     * Common implementation for inverse erf and erfc functions.
+     *
+     * @param p P-value
+     * @param q Q-value (1-p)
+     * @return the inverse
+     */
+    private static double erfInvImp(double p, double q) {
+        double result = 0;
+
+        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 optimised 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
+            //
+            final float Y = 0.0891314744949340820313f;
+            double P;
+            P =  -0.00538772965071242932965;
+            P =   0.00822687874676915743155 + P * p;
+            P =    0.0219878681111168899165 + P * p;
+            P =   -0.0365637971411762664006 + P * p;
+            P =   -0.0126926147662974029034 + P * p;
+            P =    0.0334806625409744615033 + P * p;
+            P =  -0.00836874819741736770379 + P * p;
+            P = -0.000508781949658280665617 + P * p;
+            double Q;
+            Q = 0.000886216390456424707504;
+            Q = -0.00233393759374190016776 + Q * p;
+            Q =   0.0795283687341571680018 + Q * p;
+            Q =  -0.0527396382340099713954 + Q * p;
+            Q =    -0.71228902341542847553 + Q * p;
+            Q =    0.662328840472002992063 + Q * p;
+            Q =     1.56221558398423026363 + Q * p;
+            Q =    -1.56574558234175846809 + Q * p;
+            Q =   -0.970005043303290640362 + Q * p;
+            Q =                        1.0 + Q * p;
+            final double g = p * (p + 10);
+            final double r = P / Q;
+            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 optimised 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
+            //
+            final float Y = 2.249481201171875f;
+            final double xs = q - 0.25f;
+            double P;
+            P =  -3.67192254707729348546;
+            P =   21.1294655448340526258 + P * xs;
+            P =    17.445385985570866523 + P * xs;
+            P =  -44.6382324441786960818 + P * xs;
+            P =  -18.8510648058714251895 + P * xs;
+            P =   17.6447298408374015486 + P * xs;
+            P =   8.37050328343119927838 + P * xs;
+            P =  0.105264680699391713268 + P * xs;
+            P = -0.202433508355938759655 + P * xs;
+            double Q;
+            Q =  1.72114765761200282724;
+            Q = -22.6436933413139721736 + Q * xs;
+            Q =  10.8268667355460159008 + Q * xs;
+            Q =  48.5609213108739935468 + Q * xs;
+            Q = -20.1432634680485188801 + Q * xs;
+            Q = -28.6608180499800029974 + Q * xs;
+            Q =   3.9713437953343869095 + Q * xs;
+            Q =  6.24264124854247537712 + Q * xs;
+            Q =                     1.0 + Q * xs;
+            final double g = Math.sqrt(-2 * Math.log(q));
+            final double r = P / Q;
+            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 optimised 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.
+            //
+            // Limit for a double: Math.sqrt(-Math.log(Double.MIN_VALUE)) = 
27.28...
+            // Branches for x >= 44 (supporting 80 and 128 bit long double) 
have been removed.
+            final double x = Math.sqrt(-Math.log(q));
+            if (x < 3) {
+                // Max error found: 1.089051e-20
+                final float Y = 0.807220458984375f;
+                final double xs = x - 1.125f;
+                double P;
+                P = -0.681149956853776992068e-9;
+                P =  0.285225331782217055858e-7 + P * xs;
+                P = -0.679465575181126350155e-6 + P * xs;
+                P =   0.00214558995388805277169 + P * xs;
+                P =    0.0290157910005329060432 + P * xs;
+                P =     0.142869534408157156766 + P * xs;
+                P =     0.337785538912035898924 + P * xs;
+                P =     0.387079738972604337464 + P * xs;
+                P =     0.117030156341995252019 + P * xs;
+                P =    -0.163794047193317060787 + P * xs;
+                P =    -0.131102781679951906451 + P * xs;
+                double Q;
+                Q =  0.01105924229346489121;
+                Q = 0.152264338295331783612 + Q * xs;
+                Q = 0.848854343457902036425 + Q * xs;
+                Q =  2.59301921623620271374 + Q * xs;
+                Q =  4.77846592945843778382 + Q * xs;
+                Q =  5.38168345707006855425 + Q * xs;
+                Q =  3.46625407242567245975 + Q * xs;
+                Q =                     1.0 + Q * xs;
+                final double R = P / Q;
+                result = Y * x + R * x;
+            } else if (x < 6) {
+                // Max error found: 8.389174e-21
+                final float Y = 0.93995571136474609375f;
+                final double xs = x - 3;
+                double P;
+                P = 0.266339227425782031962e-11;
+                P = -0.230404776911882601748e-9 + P * xs;
+                P =  0.460469890584317994083e-5 + P * xs;
+                P =  0.000157544617424960554631 + P * xs;
+                P =   0.00187123492819559223345 + P * xs;
+                P =   0.00950804701325919603619 + P * xs;
+                P =    0.0185573306514231072324 + P * xs;
+                P =  -0.00222426529213447927281 + P * xs;
+                P =   -0.0350353787183177984712 + P * xs;
+                double Q;
+                Q = 0.764675292302794483503e-4;
+                Q =  0.00263861676657015992959 + Q * xs;
+                Q =   0.0341589143670947727934 + Q * xs;
+                Q =    0.220091105764131249824 + Q * xs;
+                Q =    0.762059164553623404043 + Q * xs;
+                Q =      1.3653349817554063097 + Q * xs;
+                Q =                        1.0 + Q * xs;
+                final double R = P / Q;
+                result = Y * x + R * x;
+            } else if (x < 18) {
+                // Max error found: 1.481312e-19
+                final float Y = 0.98362827301025390625f;
+                final double xs = x - 6;
+                double P;
+                P =   0.99055709973310326855e-16;
+                P = -0.281128735628831791805e-13 + P * xs;
+                P =   0.462596163522878599135e-8 + P * xs;
+                P =   0.449696789927706453732e-6 + P * xs;
+                P =   0.149624783758342370182e-4 + P * xs;
+                P =   0.000209386317487588078668 + P * xs;
+                P =    0.00105628862152492910091 + P * xs;
+                P =   -0.00112951438745580278863 + P * xs;
+                P =    -0.0167431005076633737133 + P * xs;
+                double Q;
+                Q = 0.282243172016108031869e-6;
+                Q = 0.275335474764726041141e-4 + Q * xs;
+                Q = 0.000964011807005165528527 + Q * xs;
+                Q =   0.0160746087093676504695 + Q * xs;
+                Q =    0.138151865749083321638 + Q * xs;
+                Q =    0.591429344886417493481 + Q * xs;
+                Q =                        1.0 + Q * xs;
+                final double R = P / Q;
+                result = Y * x + R * x;
+            } else {
+                // x < 44
+                // Max error found: 5.697761e-20
+                final float Y = 0.99714565277099609375f;
+                final double xs = x - 18;
+                double P;
+                P = -0.116765012397184275695e-17;
+                P =  0.145596286718675035587e-11 + P * xs;
+                P =   0.411632831190944208473e-9 + P * xs;
+                P =   0.396341011304801168516e-7 + P * xs;
+                P =   0.162397777342510920873e-5 + P * xs;
+                P =   0.254723037413027451751e-4 + P * xs;
+                P =  -0.779190719229053954292e-5 + P * xs;
+                P =    -0.0024978212791898131227 + P * xs;
+                double Q;
+                Q = 0.509761276599778486139e-9;
+                Q = 0.144437756628144157666e-6 + Q * xs;
+                Q = 0.145007359818232637924e-4 + Q * xs;
+                Q = 0.000690538265622684595676 + Q * xs;
+                Q =   0.0169410838120975906478 + Q * xs;
+                Q =    0.207123112214422517181 + Q * xs;
+                Q =                        1.0 + Q * xs;
+                final double R = P / Q;
+                result = Y * x + R * x;
+            }
+        }
+        return result;
+    }
+}
+
diff --git 
a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErf.java
 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErf.java
index 802262a..3ffd2f4 100644
--- 
a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErf.java
+++ 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErf.java
@@ -18,13 +18,6 @@ package org.apache.commons.numbers.gamma;
 
 /**
  * Inverse of the <a href="http://mathworld.wolfram.com/Erf.html";>error 
function</a>.
- * <p>
- * This implementation is described in the paper:
- * <a 
href="http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf";>Approximating
- * the erfinv function</a> by Mike Giles, Oxford-Man Institute of Quantitative 
Finance,
- * which was published in GPU Computing Gems, volume 2, 2010.
- * The source code is available <a 
href="http://gpucomputing.net/?q=node/1828";>here</a>.
- * </p>
  */
 public final class InverseErf {
     /** Private constructor. */
@@ -35,99 +28,19 @@ public final class InverseErf {
     /**
      * Returns the inverse error function.
      *
-     * @param x Value.
+     * <p>Special cases:
+     * <ul>
+     * <li>If the argument is 0, then the result is 0.
+     * <li>If the argument is 1, then the result is positive infinity.
+     * <li>If the argument is -1, then the result is negative infinity.
+     * <li>If the argument is outside the interval {@code [-1, 1]}, then the 
result is nan.
+     * <li>If the argument is nan, then the result is nan.
+     * </ul>
+     *
+     * @param x Value (in {@code [-1, 1]})
      * @return t such that {@code x =} {@link Erf#value(double) Erf.value(t)}.
      */
     public static double value(final double x) {
-        // Beware that the logarithm argument must be
-        // computed as (1 - x) * (1 + x),
-        // it must NOT be simplified as 1 - x * x as this
-        // would induce rounding errors near the boundaries +/-1
-        double w = -Math.log((1 - x) * (1 + x));
-        double p;
-
-        if (w < 6.25) {
-            w -= 3.125;
-            p =  -3.6444120640178196996e-21;
-            p =   -1.685059138182016589e-19 + p * w;
-            p =   1.2858480715256400167e-18 + p * w;
-            p =    1.115787767802518096e-17 + p * w;
-            p =   -1.333171662854620906e-16 + p * w;
-            p =   2.0972767875968561637e-17 + p * w;
-            p =   6.6376381343583238325e-15 + p * w;
-            p =  -4.0545662729752068639e-14 + p * w;
-            p =  -8.1519341976054721522e-14 + p * w;
-            p =   2.6335093153082322977e-12 + p * w;
-            p =  -1.2975133253453532498e-11 + p * w;
-            p =  -5.4154120542946279317e-11 + p * w;
-            p =    1.051212273321532285e-09 + p * w;
-            p =  -4.1126339803469836976e-09 + p * w;
-            p =  -2.9070369957882005086e-08 + p * w;
-            p =   4.2347877827932403518e-07 + p * w;
-            p =  -1.3654692000834678645e-06 + p * w;
-            p =  -1.3882523362786468719e-05 + p * w;
-            p =    0.0001867342080340571352 + p * w;
-            p =  -0.00074070253416626697512 + p * w;
-            p =   -0.0060336708714301490533 + p * w;
-            p =      0.24015818242558961693 + p * w;
-            p =       1.6536545626831027356 + p * w;
-        } else if (w < 16.0) {
-            w = Math.sqrt(w) - 3.25;
-            p =   2.2137376921775787049e-09;
-            p =   9.0756561938885390979e-08 + p * w;
-            p =  -2.7517406297064545428e-07 + p * w;
-            p =   1.8239629214389227755e-08 + p * w;
-            p =   1.5027403968909827627e-06 + p * w;
-            p =   -4.013867526981545969e-06 + p * w;
-            p =   2.9234449089955446044e-06 + p * w;
-            p =   1.2475304481671778723e-05 + p * w;
-            p =  -4.7318229009055733981e-05 + p * w;
-            p =   6.8284851459573175448e-05 + p * w;
-            p =   2.4031110387097893999e-05 + p * w;
-            p =   -0.0003550375203628474796 + p * w;
-            p =   0.00095328937973738049703 + p * w;
-            p =   -0.0016882755560235047313 + p * w;
-            p =    0.0024914420961078508066 + p * w;
-            p =   -0.0037512085075692412107 + p * w;
-            p =     0.005370914553590063617 + p * w;
-            p =       1.0052589676941592334 + p * w;
-            p =       3.0838856104922207635 + p * w;
-        } else if (w < Double.POSITIVE_INFINITY) {
-            w = Math.sqrt(w) - 5;
-            p =  -2.7109920616438573243e-11;
-            p =  -2.5556418169965252055e-10 + p * w;
-            p =   1.5076572693500548083e-09 + p * w;
-            p =  -3.7894654401267369937e-09 + p * w;
-            p =   7.6157012080783393804e-09 + p * w;
-            p =  -1.4960026627149240478e-08 + p * w;
-            p =   2.9147953450901080826e-08 + p * w;
-            p =  -6.7711997758452339498e-08 + p * w;
-            p =   2.2900482228026654717e-07 + p * w;
-            p =  -9.9298272942317002539e-07 + p * w;
-            p =   4.5260625972231537039e-06 + p * w;
-            p =  -1.9681778105531670567e-05 + p * w;
-            p =   7.5995277030017761139e-05 + p * w;
-            p =  -0.00021503011930044477347 + p * w;
-            p =  -0.00013871931833623122026 + p * w;
-            p =       1.0103004648645343977 + p * w;
-            p =       4.8499064014085844221 + p * w;
-        } else if (w == Double.POSITIVE_INFINITY) {
-            // this branch does not appears in the original code, it
-            // was added because the previous branch does not handle
-            // x = +/-1 correctly. In this case, w is positive infinity
-            // and as the first coefficient (-2.71e-11) is negative.
-            // Once the first multiplication is done, p becomes negative
-            // infinity and remains so throughout the polynomial evaluation.
-            // So the branch above incorrectly returns negative infinity
-            // instead of the correct positive infinity.
-            p = Double.POSITIVE_INFINITY;
-        } else {
-            // this branch does not appears in the original code, it
-            // occurs when the input is NaN or not in the range [-1, 1].
-            return Double.NaN;
-        }
-
-        return p * x;
+        return BoostErf.erfInv(x);
     }
 }
-
diff --git 
a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErfc.java
 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErfc.java
index 60a8e7e..26ea355 100644
--- 
a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErfc.java
+++ 
b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/InverseErfc.java
@@ -18,13 +18,6 @@ package org.apache.commons.numbers.gamma;
 
 /**
  * Inverse of the <a 
href="http://mathworld.wolfram.com/Erfc.html";>complementary error function</a>.
- * <p>
- * This implementation is described in the paper:
- * <a 
href="http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf";>Approximating
- * the erfinv function</a> by Mike Giles, Oxford-Man Institute of Quantitative 
Finance,
- * which was published in GPU Computing Gems, volume 2, 2010.
- * The source code is available <a 
href="http://gpucomputing.net/?q=node/1828";>here</a>.
- * </p>
  */
 public final class InverseErfc {
     /** Private constructor. */
@@ -35,10 +28,19 @@ public final class InverseErfc {
     /**
      * Returns the inverse complementary error function.
      *
-     * @param x Value.
+     * <p>Special cases:
+     * <ul>
+     * <li>If the argument is 1, then the result is 0.
+     * <li>If the argument is 0, then the result is positive infinity.
+     * <li>If the argument is 2, then the result is negative infinity.
+     * <li>If the argument is outside the interval {@code [0, 2]}, then the 
result is nan.
+     * <li>If the argument is nan, then the result is nan.
+     * </ul>
+     *
+     * @param x Value (in {@code [0, 2]})
      * @return t such that {@code x =} {@link Erfc#value(double) 
Erfc.value(t)}.
      */
     public static double value(double x) {
-        return InverseErf.value(1 - x);
+        return BoostErf.erfcInv(x);
     }
 }
diff --git 
a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/BoostErfTest.java
 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/BoostErfTest.java
new file mode 100644
index 0000000..8f2a6f0
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/BoostErfTest.java
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.gamma;
+
+import java.math.BigDecimal;
+import java.util.function.DoubleUnaryOperator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvFileSource;
+import org.junit.jupiter.params.provider.CsvSource;
+
+/**
+ * Tests for {@link BoostErf}.
+ *
+ * <p>Note: Some resource data files used in these tests have been extracted
+ * from the Boost test files for the Erf functions.
+ */
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class BoostErfTest {
+    // Test order:
+    // Any methods not annotated with @Order will be executed as if using int 
max value.
+    // This class uses the @Order annotation to build the RMS values before 
asserting
+    // the max RMS. For convenience when using reporting the ulp errors this 
is done
+    // in the order of the class methods.
+
+    /**
+     * Define the function to test.
+     * This is a utility to identify if the function is an odd function: f(x) 
= -f(-x).
+     */
+    private enum TestFunction {
+        ERF_INV(BoostErf::erfInv, true),
+        ERFC_INV(BoostErf::erfcInv, false);
+
+        /** The function. */
+        private final DoubleUnaryOperator fun;
+
+        /** Odd function flag: f(x) = -f(-x). */
+        private final boolean odd;
+
+        /**
+         * @param fun function to test
+         * @param odd true if an odd function
+         */
+        TestFunction(DoubleUnaryOperator fun, boolean odd) {
+            this.fun = fun;
+            this.odd = odd;
+        }
+
+        /**
+         * @return the function
+         */
+        DoubleUnaryOperator getFunction() {
+            return fun;
+        }
+
+        /**
+         * @return true if an odd function: f(x) = -f(-x)
+         */
+        boolean isOdd() {
+            return odd;
+        }
+    }
+
+    /**
+     * Define the test cases for each resource file.
+     * This encapsulates the function to test, the expected maximum and RMS 
error, and
+     * methods to accumulate error and compute RMS error.
+     *
+     * <h2>Note on accuracy</h2>
+     *
+     * <p>The Boost functions use the default policy of internal promotion
+     * of double to long double if it offers more precision. Code comments
+     * in the implementations for the maximum error are using the defaults with
+     * promotion enabled where the error is 'effectively zero'. Java does not
+     * support long double computation. Tolerances have been set to allow 
tests to
+     * pass. Spot checks on larger errors have been verified against the 
reference
+     * implementation compiled with promotion of double 
<strong>disabled</strong>.
+     *
+     * @see <a 
href="https://www.boost.org/doc/libs/1_77_0/libs/math/doc/html/math_toolkit/relative_error.html";>Relative
 error</a>
+     * @see <a 
href="https://www.boost.org/doc/libs/1_77_0/libs/math/doc/html/math_toolkit/pol_tutorial/policy_tut_defaults.html";>Policy
 defaults</a>
+     */
+    private enum TestCase {
+        /** Inverse Erf Boost data. */
+        ERF_INV(TestFunction.ERF_INV, 1.8, 0.65),
+        /** Inverse Erfc Boost data. */
+        ERFC_INV(TestFunction.ERFC_INV, 1.8, 0.65),
+        /** Inverse Erfc Boost big data. */
+        ERFC_INV_BIG(TestFunction.ERFC_INV, 1.8, 0.5),
+        /** Inverse Erf limit data. */
+        ERF_INV_LIMIT(TestFunction.ERF_INV, 1.4, 0.5),
+        /** Inverse Erfc limit data. */
+        ERFC_INV_LIMIT(TestFunction.ERFC_INV, 1.2, 0.5);
+
+        /** Sum of the squared ULP error and count n. */
+        private final RMS rms = new RMS();
+
+        /** The function. */
+        private final TestFunction fun;
+
+        /** The maximum allowed ulp. */
+        private final double maxUlp;
+
+        /** The maximum allowed RMS ulp. */
+        private final double rmsUlp;
+
+        /**
+         * @param fun function to test
+         * @param maxUlp maximum allowed ulp
+         * @param rmsUlp maximum allowed RMS ulp
+         */
+        TestCase(TestFunction fun, double maxUlp, double rmsUlp) {
+            this.fun = fun;
+            this.maxUlp = maxUlp;
+            this.rmsUlp = rmsUlp;
+        }
+
+        /**
+         * @return function to test
+         */
+        DoubleUnaryOperator getFunction() {
+            return fun.getFunction();
+        }
+
+        /**
+         * @return true if an odd function
+         */
+        boolean isOdd() {
+            return fun.isOdd();
+        }
+
+        /**
+         * @param ulp error in ulp
+         */
+        void addError(double ulp) {
+            rms.add(ulp);
+        }
+
+        /**
+         * @return Root Mean Squared measured error
+         */
+        double getRMSError() {
+            return rms.getRMS();
+        }
+
+        /**
+         * @return maximum measured error
+         */
+        double getMaxError() {
+            return rms.getMax();
+        }
+
+        /**
+         * @return maximum allowed error
+         */
+        double getTolerance() {
+            return maxUlp;
+        }
+
+        /**
+         * @return maximum allowed RMS error
+         */
+        double getRmsTolerance() {
+            return rmsUlp;
+        }
+    }
+
+    /**
+     * Class to compute the root mean squared error (RMS).
+     * @see <a 
href="https://en.wikipedia.org/wiki/Root_mean_square";>Wikipedia: RMS</a>
+     */
+    private static class RMS {
+        private double ss;
+        private double max;
+        private int n;
+
+        /**
+         * @param x Value (assumed to be positive)
+         */
+        void add(double x) {
+            // Overflow is not supported.
+            // Assume the expected and actual are quite close when measuring 
the RMS.
+            ss += x * x;
+            n++;
+            // Assume absolute error when detecting the maximum
+            max = max < x ? x : max;
+        }
+
+        /**
+         * Gets the maximum error.
+         *
+         * <p>This is not used for assertions. It can be used to set maximum 
ULP thresholds
+         * for test data if the TestUtils.assertEquals method is used with a 
large maxUlps
+         * to measure the ulp (and effectively ignore failures) and the 
maximum reported
+         * as the end of testing.
+         *
+         * @return maximum error
+         */
+        double getMax() {
+            return max;
+        }
+
+        /**
+         * Gets the root mean squared error (RMS).
+         *
+         * <p> Note: If no data has been added this will return 0/0 = nan.
+         * This prevents using in assertions without adding data.
+         *
+         * @return root mean squared error (RMS)
+         */
+        double getRMS() {
+            return Math.sqrt(ss / n);
+        }
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        "0, 0",
+        // Odd function
+        "-0.0, -0.0",
+        "1, Infinity",
+        "-1, -Infinity",
+        "NaN, NaN",
+        // Domain errors do not throw
+        "-1.1, NaN",
+        "1.1, NaN",
+    })
+    void testInverseErfEdgeCases(double p, double z) {
+        Assertions.assertEquals(z, BoostErf.erfInv(p));
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        "1, 0",
+        "0, Infinity",
+        "2, -Infinity",
+        "NaN, NaN",
+        // Domain errors do not throw
+        "-0.1, NaN",
+        "2.1, NaN",
+    })
+    void testInverseErfcEdgeCases(double p, double z) {
+        Assertions.assertEquals(z, BoostErf.erfcInv(p));
+    }
+
+    @ParameterizedTest
+    @Order(1)
+    @CsvFileSource(resources = "erf_inv_data.csv")
+    void testInverseErf(double p, BigDecimal z) {
+        assertErf(TestCase.ERF_INV, p, z);
+    }
+
+    @Test
+    @Order(2010)
+    void testInverseErfRMS() {
+        assertRms(TestCase.ERF_INV);
+    }
+
+    @ParameterizedTest
+    @Order(1)
+    @CsvFileSource(resources = "erfc_inv_data.csv")
+    void testInverseErfc(double q, BigDecimal z) {
+        assertErf(TestCase.ERFC_INV, q, z);
+    }
+
+    @Test
+    @Order(2020)
+    void testInverseErfcRMS() {
+        assertRms(TestCase.ERFC_INV);
+    }
+
+    @ParameterizedTest
+    @Order(1)
+    @CsvFileSource(resources = "erfc_inv_big_data.csv")
+    void testInverseErfcBig(double q, BigDecimal z) {
+        assertErf(TestCase.ERFC_INV_BIG, q, z);
+    }
+
+    @Test
+    @Order(2030)
+    void testInverseErfcBigRMS() {
+        assertRms(TestCase.ERFC_INV_BIG);
+    }
+
+    @ParameterizedTest
+    @Order(1)
+    @CsvFileSource(resources = "erf_inv_limit_data.csv")
+    void testInverseErfLimit(double p, BigDecimal z) {
+        assertErf(TestCase.ERF_INV_LIMIT, p, z);
+    }
+
+    @Test
+    @Order(2040)
+    void testInverseErfLimitRMS() {
+        assertRms(TestCase.ERF_INV_LIMIT);
+    }
+
+    @ParameterizedTest
+    @Order(1)
+    @CsvFileSource(resources = "erfc_inv_limit_data.csv")
+    void testInverseErfcLimit(double q, BigDecimal z) {
+        assertErf(TestCase.ERFC_INV_LIMIT, q, z);
+    }
+
+    @Test
+    @Order(2050)
+    void testInverseErfcLimitRMS() {
+        assertRms(TestCase.ERFC_INV_LIMIT);
+    }
+
+    /**
+     * Round-trip test of the inverse erf and then the erf to return to the 
original value.
+     */
+    @Test
+    void testErfRoundTrip() {
+        assertRoundTrip("erf(erfInv(x))",
+            x -> Erf.value(BoostErf.erfInv(x)),
+            // Inverse Erf domain: [-1, 1]
+            -0.95, 1, 0.125,
+            5L, 2.99);
+    }
+
+    /**
+     * Round-trip test of the inverse erfc and then the erfc to return to the 
original value.
+     */
+    @Test
+    void testErfcRoundTrip() {
+        assertRoundTrip("erfc(erfcInv(x))",
+            x -> Erfc.value(BoostErf.erfcInv(x)),
+            // Inverse Erfc domain: [0, 2]
+            0.125, 2, 0.125,
+            15L, 3.99);
+    }
+
+    /**
+     * Test a round-trip function (foward and reverse) returns to the original 
value.
+     *
+     * @param name Test name
+     * @param fun Round-trip function
+     * @param low Low bound to test
+     * @param high Upper bound to test
+     * @param increment Increment between bounds
+     * @param tolerance Maximum ULP tolerance
+     * @param rmsUlp Maximum RMS ULP
+     */
+    private static void assertRoundTrip(String name,
+            DoubleUnaryOperator fun,
+            double low, double high, double increment,
+            long tolerance, double rmsUlp) {
+        final RMS data = new RMS();
+        for (double p = low; p <= high; p += increment) {
+            final double pp = fun.applyAsDouble(p);
+            TestUtils.assertEquals(p, pp, tolerance, ulp -> data.add(ulp), () 
-> name);
+        }
+        assertRms(name, data, rmsUlp);
+    }
+
+    /**
+     * Assert the function using extended precision.
+     *
+     * @param tc Test case
+     * @param x Input value
+     * @param expected Expected value
+     */
+    private static void assertErf(TestCase tc, double x, BigDecimal expected) {
+        final double actual = tc.getFunction().applyAsDouble(x);
+        TestUtils.assertEquals(expected, actual, tc.getTolerance(), 
tc::addError,
+            () -> tc + " x=" + x);
+        if (tc.isOdd()) {
+            // Use a delta of 0.0 to allow -0.0 == 0.0
+            Assertions.assertEquals(actual, 
-tc.getFunction().applyAsDouble(-x), 0.0, "odd function: f(x) = -f(-x)");
+        }
+    }
+
+    /**
+     * Assert the Root Mean Square (RMS) error of the function is below the 
allowed maximum.
+     *
+     * @param tc Test case
+     */
+    private static void assertRms(TestCase tc) {
+        final double rms = tc.getRMSError();
+        debugRms(tc.toString(), tc.getMaxError(), rms);
+        Assertions.assertTrue(rms <= tc.getRmsTolerance(),
+            () -> String.format("%s RMS %s < %s", tc, rms, 
tc.getRmsTolerance()));
+    }
+
+    /**
+     * Assert the Root Mean Square (RMS) error of the function is below the 
allowed maximum.
+     *
+     * @param name Test name
+     * @param data Test data
+     * @param rmsTolerance RMS tolerance
+     */
+    private static void assertRms(String name, RMS data, double rmsTolerance) {
+        final double rms = data.getRMS();
+        debugRms(name, data.getMax(), rms);
+        Assertions.assertTrue(rms <= rmsTolerance,
+            () -> String.format("%s RMS %s < %s", name, rms, rmsTolerance));
+    }
+
+    /**
+     * Output the maximum and RMS ulp for the named test.
+     * Used for reporting the errors and setting appropriate test tolerances.
+     * This is relevant across different JDK implementations where the 
java.util.Math
+     * functions used in BoostErf may compute to different accuracy.
+     *
+     * @param name Test name
+     * @param maxUlp Maximum ulp
+     * @param rmsUlp RMS ulp
+     */
+    private static void debugRms(String name, double maxUlp, double rmsUlp) {
+        // CHECKSTYLE: stop regexp
+        // Debug output of max and RMS error.
+        //System.out.printf("%-25s   max %10.6g   RMS %10.6g%n", name, maxUlp, 
rmsUlp);
+    }
+}
diff --git 
a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtils.java
 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtils.java
new file mode 100644
index 0000000..5c4a269
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtils.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.gamma;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Assertions;
+
+/**
+ * Test utilities.
+ */
+final class TestUtils {
+    /** Positive zero bits. */
+    private static final long POSITIVE_ZERO_DOUBLE_BITS = 
Double.doubleToRawLongBits(+0.0);
+    /** Negative zero bits. */
+    private static final long NEGATIVE_ZERO_DOUBLE_BITS = 
Double.doubleToRawLongBits(-0.0);
+    /** Set this to true to report all deviations to System out when the 
maximum ULPs is negative. */
+    private static boolean reportAllDeviations = false;
+
+    /** Private constructor. */
+    private TestUtils() {
+        // intentionally empty.
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided units of least 
precision.
+     * The maximum count of numbers allowed between the two values is {@code 
maxUlps - 1}.
+     *
+     * <p>The values -0.0 and 0.0 are considered equal.
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and 
ignore
+     * failures.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @return ulp difference between the values (always positive; may be 
truncated to Long.MAX_VALUE)
+     */
+    static long assertEquals(double expected, double actual, long maxUlps) {
+        return assertEquals(expected, actual, maxUlps, null, 
(Supplier<String>) null);
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided units of least
+     * precision. The maximum count of numbers allowed between the two values 
is
+     * {@code maxUlps - 1}.
+     *
+     * <p>The values -0.0 and 0.0 are considered equal.
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and
+     * ignore failures.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @param error Consumer for the ulp difference between the values (always 
positive)
+     * @param msg failure message
+     * @return ulp difference between the values (always positive; may be 
truncated to Long.MAX_VALUE)
+     */
+    static long assertEquals(double expected, double actual, long maxUlps,
+            LongConsumer error, Supplier<String> msg) {
+        final long e = Double.doubleToLongBits(expected);
+        final long a = Double.doubleToLongBits(actual);
+
+        // Code adapted from Precision#equals(double, double, int) so we 
maintain the delta
+        // for the message and return it for reporting.
+
+        long delta;
+        boolean equal;
+        if (e == a) {
+            // Binary equal
+            equal = true;
+            delta = 0;
+        } else if ((a ^ e) < 0L) {
+            // The difference is the count of numbers between each and zero.
+            // This makes -0.0 and 0.0 equal.
+            long d1;
+            long d2;
+            if (a < e) {
+                d1 = e - POSITIVE_ZERO_DOUBLE_BITS;
+                d2 = a - NEGATIVE_ZERO_DOUBLE_BITS;
+            } else {
+                d1 = a - POSITIVE_ZERO_DOUBLE_BITS;
+                d2 = e - NEGATIVE_ZERO_DOUBLE_BITS;
+            }
+            // This may overflow but we report it using an unsigned formatter.
+            delta = d1 + d2;
+            if (delta < 0) {
+                // Overflow
+                equal = false;
+            } else {
+                // Allow input of a negative maximum ULPs
+                equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps);
+            }
+        } else {
+            delta = Math.abs(e - a);
+            // Allow input of a negative maximum ULPs
+            equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps);
+        }
+
+        // DEBUG:
+        if (maxUlps < 0) {
+            // CHECKSTYLE: stop Regex
+            if (!equal || reportAllDeviations) {
+                System.out.printf("%sexpected <%s> != actual <%s> (ulps=%s)%n",
+                    prefix(msg), expected, actual, 
Long.toUnsignedString(delta));
+            }
+            // CHECKSTYLE: resume Regex
+        } else if (!equal) {
+            Assertions.fail(String.format("%sexpected <%s> != actual <%s> 
(ulps=%s)",
+                prefix(msg), expected, actual, Long.toUnsignedString(delta)));
+        }
+
+        // This may have overflowed.
+        delta = delta < 0 ? Long.MAX_VALUE : delta;
+        if (error != null) {
+            error.accept(delta);
+        }
+        return delta;
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided units of least 
precision.
+     *
+     * <p>This method is for values that can be computed to arbitrary 
precision.
+     * It raises an exception when the actual value is not finite and the 
expected value
+     * has a non-infinite representation; or the actual value is finite and 
the expected
+     * value has a infinite representation. In this case the computed ulp 
difference
+     * is infinite.
+     *
+     * <p>This method expresses the error relative the units in the last place 
of the
+     * expected value when converted to a {@code double} type
+     * (see {@link #assertEquals(BigDecimal, double, double, DoubleConsumer, 
Supplier)} for details).
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and 
ignore
+     * failures.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @return ulp difference between the values (always positive)
+     */
+    static double assertEquals(BigDecimal expected, double actual, double 
maxUlps) {
+        return assertEquals(expected, actual, maxUlps, null, 
(Supplier<String>) null);
+    }
+
+    /**
+     * Assert the two numbers are equal within the provided units of least
+     * precision.
+     *
+     * <p>This method is for values that can be computed to arbitrary 
precision. It
+     * raises an exception when the actual value is not finite and the expected
+     * value has a non-infinite representation; or the actual value is finite 
and
+     * the expected value has a infinite representation. In this case the 
computed
+     * ulp difference is infinite.
+     *
+     * <p>This method expresses the error relative the units in the last place 
(ulp)
+     * of the expected value when converted to a {@code double} type. If the 
actual
+     * value equals the expected value the error is 0. Otherwise the error is
+     * computed relative to the ulp of the expected value. The minimum non-zero
+     * value for the error is 0.5. A ulp of < 1.0 indicates the value is the 
closest
+     * value to the result that is not exact.
+     *
+     * <pre>
+     * ulp          -1               0               1               2
+     * --------------|---------------|---------------|---------------|--------
+     *                           ^
+     *                           |
+     *                        expected
+     *
+     *               a               b               c
+     *
+     * a = 0.75 ulp
+     * b = 0    ulp
+     * c = 1.25 ulp
+     * </pre>
+     *
+     * <p>Set {@code maxUlps} to negative to report the ulps to the stdout and
+     * ignore failures.
+     *
+     * @param expected expected value
+     * @param actual actual value
+     * @param maxUlps maximum units of least precision between the two values
+     * @param error Consumer for the ulp difference between the values (always 
positive)
+     * @param msg failure message
+     * @return ulp difference between the values (always positive)
+     */
+    static double assertEquals(BigDecimal expected, double actual, double 
maxUlps,
+            DoubleConsumer error, Supplier<String> msg) {
+        final double e = expected.doubleValue();
+
+        double delta;
+        boolean equal;
+        if (e == actual) {
+            // Binary equal. This will match infinity if expected is very 
large.
+            equal = true;
+            delta = 0;
+        } else if (!Double.isFinite(e) || !Double.isFinite(actual)) {
+            // No representable delta between infinite and non-infinite values
+            equal = false;
+            delta = Double.POSITIVE_INFINITY;
+        } else {
+            // Two finite numbers
+            delta = expected.subtract(new BigDecimal(actual)).abs()
+                        .divide(new BigDecimal(Math.ulp(e)), 
MathContext.DECIMAL64).doubleValue();
+            // Allow input of a negative maximum ULPs
+            equal = delta <= Math.abs(maxUlps);
+        }
+
+        if (error != null) {
+            error.accept(delta);
+        }
+
+        // DEBUG:
+        if (maxUlps < 0) {
+            // CHECKSTYLE: stop Regex
+            if (!equal || reportAllDeviations) {
+                System.out.printf("%sexpected <%s> != actual <%s> (ulps=%s)%n",
+                    prefix(msg), expected, actual, delta);
+            }
+            // CHECKSTYLE: resume Regex
+        } else if (!equal) {
+            Assertions.fail(String.format("%sexpected <%s> != actual <%s> 
(ulps=%s)",
+                prefix(msg), expected, actual, delta));
+        }
+
+        return delta;
+    }
+
+    /**
+     * Get the prefix for the message.
+     *
+     * @param msg Message supplier
+     * @return the prefix
+     */
+    private static String prefix(Supplier<String> msg) {
+        return msg == null ? "" : msg.get() + ": ";
+    }
+}
diff --git 
a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtilsTest.java
 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtilsTest.java
new file mode 100644
index 0000000..6447735
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TestUtilsTest.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.gamma;
+
+import java.math.BigDecimal;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link TestUtils}.
+ * This verifies the custom ULP assertions function as expected.
+ */
+class TestUtilsTest {
+    @Test
+    void testAssertEquals() {
+        final double x = 1.23;
+        final double ulp = Math.ulp(x);
+        Assertions.assertEquals(0, TestUtils.assertEquals(x, x, 0));
+        Assertions.assertEquals(0, TestUtils.assertEquals(x, x, 1));
+        Assertions.assertEquals(0, TestUtils.assertEquals(x, x, 30));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(x, x + ulp, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(x, x + ulp, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(x, x - ulp, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(x, x + ulp, 30));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(x, x + 30 * ulp, 29));
+        Assertions.assertEquals(30, TestUtils.assertEquals(x, x + 30 * ulp, 
30));
+        Assertions.assertEquals(30, TestUtils.assertEquals(x, x - 30 * ulp, 
30));
+
+        // Check order and sign
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(-x - ulp, x, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(-x - ulp, -x, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(x - ulp, x, 1));
+        Assertions.assertEquals(2, TestUtils.assertEquals(x - ulp, x + ulp, 
2));
+
+        // Opposite signs
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(-x, x, 500000));
+    }
+
+    @Test
+    void testAssertEqualsZero() {
+        // These are not binary equal but for numeric purposes they are 
treated as equal
+        final double[] zero = {-0.0, 0.0};
+        for (final double a : zero) {
+            for (final double b : zero) {
+                Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 0));
+                Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 1));
+                Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 30));
+            }
+        }
+
+        // Difference from zero
+        final double x = Double.MIN_VALUE;
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(0.0, x, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(0.0, x, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(-0.0, x, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(0.0, -x, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(-0.0, -x, 1));
+        Assertions.assertEquals(2, TestUtils.assertEquals(0.0, 2 * x, 2));
+        Assertions.assertEquals(2, TestUtils.assertEquals(-0.0, 2 * x, 2));
+    }
+
+    @Test
+    void testAssertEqualsEdgeCases() {
+        final double nan = Double.NaN;
+        final double inf = Double.POSITIVE_INFINITY;
+        final double max = Double.MAX_VALUE;
+        Assertions.assertEquals(0, TestUtils.assertEquals(nan, nan, 0));
+        Assertions.assertEquals(0, TestUtils.assertEquals(inf, inf, 0));
+        Assertions.assertEquals(0, TestUtils.assertEquals(max, max, 0));
+
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(nan, inf, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(nan, max, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(inf, max, 0));
+
+        Assertions.assertEquals(1, TestUtils.assertEquals(inf, max, 1));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(inf, -inf, 0));
+    }
+
+    @Test
+    void testBigDecimalAssertEquals() {
+        final double x = 1.23;
+        final double ulp = Math.ulp(x);
+
+        final BigDecimal bdx = new BigDecimal(x);
+
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdx, x, 0));
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdx, x, 1));
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdx, x, 30));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdx, x + ulp, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(bdx, x + ulp, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(bdx, x - ulp, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(bdx, x + ulp, 30));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdx, x + 30 * ulp, 29));
+        Assertions.assertEquals(30, TestUtils.assertEquals(bdx, x + 30 * ulp, 
30));
+        Assertions.assertEquals(30, TestUtils.assertEquals(bdx, x - 30 * ulp, 
30));
+
+        // Opposite signs
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdx.negate(), x, 500000));
+
+        // High precision: Add 1/4 ulp to the value:
+        //    ---|----------|----------|-----
+        //       -1         x  |       +1
+        //                     y
+        final BigDecimal bdy = bdx.add(new 
BigDecimal(ulp).divide(BigDecimal.valueOf(4)));
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdy, x, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdy, x + ulp, 0));
+        Assertions.assertEquals(0.75, TestUtils.assertEquals(bdy, x + ulp, 1));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdy, x - ulp, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdy, x - ulp, 1));
+        Assertions.assertEquals(1.25, TestUtils.assertEquals(bdy, x - ulp, 
1.5), 1e-16);
+    }
+
+    @Test
+    void testBigDecimalAssertEqualsZero() {
+        // These are not binary equal but for numeric purposes they are 
treated as equal
+        final double[] zero = {-0.0, 0.0};
+        final BigDecimal a = BigDecimal.ZERO;
+        for (final double b : zero) {
+            Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 0));
+            Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 1));
+            Assertions.assertEquals(0, TestUtils.assertEquals(a, b, 30));
+        }
+
+        // Difference from zero
+        final double x = Double.MIN_VALUE;
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(a, x, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(a, x, 1));
+        Assertions.assertEquals(1, TestUtils.assertEquals(a, -x, 1));
+        Assertions.assertEquals(2, TestUtils.assertEquals(a, 2 * x, 2));
+        Assertions.assertEquals(2, TestUtils.assertEquals(a, -2 * x, 2));
+    }
+
+    @Test
+    void testBigDecimalAssertEqualsEdgeCases() {
+        final double nan = Double.NaN;
+        final double inf = Double.POSITIVE_INFINITY;
+        final double max = Double.MAX_VALUE;
+
+        final BigDecimal bdinf = new BigDecimal("1e310");
+        final BigDecimal bdmax = new BigDecimal(max);
+
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdinf, inf, 0));
+        Assertions.assertEquals(0, TestUtils.assertEquals(bdmax, max, 0));
+
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdinf, max, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdinf, nan, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdmax, inf, 0));
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdmax, nan, 0));
+
+        final double x = Math.nextDown(max);
+        Assertions.assertThrows(AssertionError.class, () -> 
TestUtils.assertEquals(bdmax, x, 0));
+        Assertions.assertEquals(1, TestUtils.assertEquals(bdmax, x, 1));
+    }
+}
diff --git 
a/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_data.csv
 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_data.csv
new file mode 100644
index 0000000..483b8b1
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_data.csv
@@ -0,0 +1,122 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# (C) Copyright John Maddock 2006-7.
+# Use, modification and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+# Extracted from: boost/libs/math/test/erf_inv_data.ipp
+
+-0.990433037281036376953125,-1.832184533179510927322805923563700329767
+-0.936334311962127685546875,-1.311339282092737086640055105484822812599
+-0.931107819080352783203125,-1.286316461685184857889337272829270644576
+-0.928576648235321044921875,-1.274755355308702535979544636706295190547
+-0.92711746692657470703125,-1.268242390461126936128446743938528753699
+-0.907657206058502197265625,-1.190178701802009872651528128114629153367
+-0.89756715297698974609375,-1.154826929034364581020764782706068245251
+-0.80573642253875732421875,-0.917873491512746130598510739795171676962
+-0.804919183254241943359375,-0.9161942446913841771580833184012189161559
+-0.780276477336883544921875,-0.867806431357551134410815525153998291827
+-0.775070965290069580078125,-0.8580919924152284867224297625328485999768
+-0.7496345043182373046875,-0.8127924290810407931222432768905514928867
+-0.74820673465728759765625,-0.8103475955423936417307157186107256388648
+-0.74602639675140380859375,-0.806632688757205819001462030577472890805
+-0.72904598712921142578125,-0.7784313874823551106598826200232666844639
+-0.7162272930145263671875,-0.7579355487144890063429377586715787838945
+-0.701772034168243408203125,-0.7355614373173595299453301005770428516518
+-0.68477380275726318359375,-0.7101588399949052872532446197423489724803
+-0.657626628875732421875,-0.6713881533266128640126408255047881638389
+-0.652269661426544189453125,-0.6639738263692763456669198307149427734317
+-0.6262547969818115234375,-0.6289572925573171740836428308746584439674
+-0.62323606014251708984375,-0.6249936843093662471116097431474787933967
+-0.57958185672760009765625,-0.5697131213589784467617578394703976041604
+-0.576151371002197265625,-0.5655172244109430153330195150336141500139
+-0.5579319000244140625,-0.5435569422159360687847790186563654276103
+-0.446154057979583740234375,-0.4186121208546731033057205459902879301199
+-0.44300353527069091796875,-0.4152898953738801984047941692529271391195
+-0.40594112873077392578125,-0.3768620801611051992528860948080812212023
+-0.396173775196075439453125,-0.3669220210390825311547962776125822899061
+-0.38366591930389404296875,-0.3542977152760563782151668726041057557165
+-0.36689913272857666015625,-0.3375493847053488720470432821496358279516
+-0.365801036357879638671875,-0.3364591774366710656954166264654945559873
+-0.277411997318267822265625,-0.2510244067029671790889794981353227476998
+-0.236883103847503662109375,-0.213115119330839975829499967157244997714
+-0.215545952320098876953125,-0.1934073617841803428235669261097060281642
+-0.202522933483123779296875,-0.1814532246720147926398927046057793150106
+-0.18253767490386962890625,-0.1632073953550647568421821286058243218715
+-0.156477451324462890625,-0.1395756320903277910768376053314442757507
+-0.1558246612548828125,-0.1389857795955756484965030151195660030168
+-0.12251126766204833984375,-0.109002961098867662134935094105847496074
+-0.1088275909423828125,-0.09674694516640724629590870677194632943569
+-0.08402168750762939453125,-0.07460044047654119877070700345343119035515
+-0.05048263072967529296875,-0.04476895818328636312384562686715995170129
+-0.029248714447021484375,-0.0259268064334840921104659134138093242797
+-0.02486217021942138671875,-0.02203709146986755832638577823832075055744
+-0.02047121524810791015625,-0.01814413302702029459097557481591610553903
+-0.018821895122528076171875,-0.01668201759439857888105181293763417899072
+0.0073254108428955078125,0.006492067534753215749601670217642082465642
+0.09376299381256103515625,0.08328747254794857150987333986733043734817
+0.0944411754608154296875,0.08389270963798942778622198997355058545872
+0.264718532562255859375,0.2390787735821979528028028789569770109829
+0.27952671051025390625,0.2530214201700340392837551955289041822603
+0.29262602329254150390625,0.2654374523135675523971788948011709467352
+0.3109557628631591796875,0.282950508503826367238408926581528085458
+0.31148135662078857421875,0.2834552014554130441860525970673030809536
+0.32721102237701416015625,0.2986277427848421570858990348074985028421
+0.3574702739715576171875,0.3282140305634627092431945088114761850208
+0.362719058990478515625,0.3334035993712283467959295804559099468454
+0.3896572589874267578125,0.3603304982893212173104266596348905175268
+0.4120922088623046875,0.3831602323665075533579267768785894144888
+0.41872966289520263671875,0.3899906753567599452444107492361433402154
+0.45167791843414306640625,0.4244594733907945411184647153213164209335
+0.48129451274871826171875,0.4563258063707025027210352963461819167707
+0.4862649440765380859375,0.4617640058971775089811390737537561779898
+0.50937330722808837890625,0.4874174763856674076219106695373814892182
+0.5154802799224853515625,0.4943041993872143888987628020569772222018
+0.52750003337860107421875,0.5079978091910991117615000459548117088362
+0.53103363513946533203125,0.5120597685873370942783226077302881881069
+0.58441460132598876953125,0.5756584292527058478710392476034273328569
+0.5879499912261962890625,0.5800336103175463592377907341030447077804
+0.59039986133575439453125,0.5830784871670823806198622501806646778319
+0.59455978870391845703125,0.588273673825686998734497652983815773459
+0.59585726261138916015625,0.5899005483108011364541949539839185473259
+0.5962116718292236328125,0.5903454775096607218832535637355431851718
+0.6005609035491943359375,0.5958247243549040349587326482492767206448
+0.6150619983673095703125,0.6143583249050861028039832921829036722514
+0.62944734096527099609375,0.6331707263097125575937994856370309207836
+0.64380657672882080078125,0.6524069265890823819975498133014027247554
+0.6469156742095947265625,0.656635855345815020063728463464436343698
+0.67001712322235107421875,0.6888269167957872563013714303376548038671
+0.6982586383819580078125,0.7302336318927408409119676651737758401138
+0.74485766887664794921875,0.8046505193013635090578266413458426260098
+0.75686132907867431640625,0.8253191678260588578995203396384711816647
+0.81158387660980224609375,0.9300427626888758122211127950646282789481
+0.826751708984375,0.9629665092443368464606966822833571908852
+0.83147108554840087890625,0.9736479209913771931387473923084901789046
+0.84174954891204833984375,0.997713670556719074960678197806799852186
+0.8679864406585693359375,1.065050516333636716777334376076374184102
+0.90044414997100830078125,1.164612422633086435501625591693259387477
+0.91433393955230712890625,1.215315881176612875682446995412738776976
+0.91501367092132568359375,1.217962731073139868794942852653058932976
+0.918984889984130859375,1.233778505900771488542027767896521427575
+0.92977702617645263671875,1.28019542575660930623179572273596558907
+0.93538987636566162109375,1.306695301483797253764522033930388453334
+0.93773555755615234375,1.318335478463913327121670503572736587296
+0.94118559360504150390625,1.33613349872692113073358883961598631154
+0.96221935749053955078125,1.468821071545234761861756248744372345584
+0.98576259613037109375,1.733272259459038694476413373595347034928
+0.9881370067596435546875,1.77921769652839903464038407684397479173
+0.99292266368865966796875,1.904368122482929779094714951471938518496
diff --git 
a/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_limit_data.csv
 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_limit_data.csv
new file mode 100644
index 0000000..1cf6883
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erf_inv_limit_data.csv
@@ -0,0 +1,129 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Erf limits: [-1, 1]
+# Odd function (f(x) = -f(-x)) so test the range [0, 1] and
+# its mirror in the unit test.
+# Test the critical regions of the range using x -> 0, x -> 0.5 and x -> 1.
+#
+# Data generated using the following matlab script:
+#
+# f = fopen('erf_inv_data2.csv', 'wt');
+#
+# % Uses variable precision arithmetic (vpa) to exactly approach 0, 0.5 and 1.
+# digits(30);
+# x = [1:10];
+# % anonymous function writes the argument and the inverse erf function value 
to file
+# pr = @(p) fprintf(f, "%.17g,%s\n", p, vpa(erfinv(p)));
+# for i = [1074:-1:1022]
+#   pr(sym(2^-i));
+# end
+# for i = flip(x)
+#   pr(sym(1/2) - sym(i*2^-54));
+# end
+# for i = x
+#   pr(sym(1/2) + sym(i*2^-53));
+# end
+# for i = flip(x)
+#   pr(sym(1) - sym(i*2^-53));
+# end
+#
+# clear pr;
+# fclose(f);
+
+4.9406564584124654e-324,4.37854278285719143435811695173e-324
+9.8813129168249309e-324,8.75708556571438286871623390345e-324
+1.9762625833649862e-323,1.75141711314287657374324678069e-323
+3.9525251667299724e-323,3.50283422628575314748649356138e-323
+7.9050503334599447e-323,7.00566845257150629497298712276e-323
+1.5810100666919889e-322,1.40113369051430125899459742455e-322
+3.1620201333839779e-322,2.8022673810286025179891948491e-322
+6.3240402667679558e-322,5.60453476205720503597838969821e-322
+1.2648080533535912e-321,1.12090695241144100719567793964e-321
+2.5296161067071823e-321,2.24181390482288201439135587928e-321
+5.0592322134143646e-321,4.48362780964576402878271175857e-321
+1.0118464426828729e-320,8.96725561929152805756542351713e-321
+2.0236928853657458e-320,1.79345112385830561151308470343e-320
+4.0473857707314917e-320,3.58690224771661122302616940685e-320
+8.0947715414629834e-320,7.17380449543322244605233881371e-320
+1.6189543082925967e-319,1.43476089908664448921046776274e-319
+3.2379086165851934e-319,2.86952179817328897842093552548e-319
+6.4758172331703867e-319,5.73904359634657795684187105097e-319
+1.2951634466340773e-318,1.14780871926931559136837421019e-318
+2.5903268932681547e-318,2.29561743853863118273674842039e-318
+5.1806537865363094e-318,4.59123487707726236547349684077e-318
+1.0361307573072619e-317,9.18246975415452473094699368155e-318
+2.0722615146145237e-317,1.83649395083090494618939873631e-317
+4.1445230292290475e-317,3.67298790166180989237879747262e-317
+8.289046058458095e-317,7.34597580332361978475759494524e-317
+1.657809211691619e-316,1.46919516066472395695151898905e-316
+3.315618423383238e-316,2.93839032132944791390303797809e-316
+6.631236846766476e-316,5.87678064265889582780607595619e-316
+1.3262473693532952e-315,1.17535612853177916556121519124e-315
+2.6524947387065904e-315,2.35071225706355833112243038248e-315
+5.3049894774131808e-315,4.70142451412711666224486076495e-315
+1.0609978954826362e-314,9.4028490282542333244897215299e-315
+2.1219957909652723e-314,1.88056980565084666489794430598e-314
+4.2439915819305446e-314,3.76113961130169332979588861196e-314
+8.4879831638610893e-314,7.52227922260338665959177722392e-314
+1.6975966327722179e-313,1.50445584452067733191835544478e-313
+3.3951932655444357e-313,3.00891168904135466383671088957e-313
+6.7903865310888714e-313,6.01782337808270932767342177914e-313
+1.3580773062177743e-312,1.20356467561654186553468435583e-312
+2.7161546124355486e-312,2.40712935123308373106936871166e-312
+5.4323092248710971e-312,4.81425870246616746213873742331e-312
+1.0864618449742194e-311,9.62851740493233492427747484662e-312
+2.1729236899484389e-311,1.92570348098646698485549496932e-311
+4.3458473798968777e-311,3.85140696197293396971098993865e-311
+8.6916947597937554e-311,7.7028139239458679394219798773e-311
+1.7383389519587511e-310,1.54056278478917358788439597546e-310
+3.4766779039175022e-310,3.08112556957834717576879195092e-310
+6.9533558078350043e-310,6.16225113915669435153758390184e-310
+1.3906711615670009e-309,1.23245022783133887030751678037e-309
+2.7813423231340017e-309,2.46490045566267774061503356073e-309
+5.5626846462680035e-309,4.92980091132535548123006712147e-309
+1.1125369292536007e-308,9.85960182265071096246013424294e-309
+2.2250738585072014e-308,1.97192036453014219249202684859e-308
+0.49999999999999944,0.476936276204469255772776918108
+0.4999999999999995,0.476936276204469317533641061662
+0.49999999999999956,0.476936276204469379294505205215
+0.49999999999999961,0.476936276204469441055369348769
+0.49999999999999967,0.476936276204469502816233492322
+0.49999999999999972,0.476936276204469564577097635876
+0.49999999999999978,0.476936276204469626337961779429
+0.49999999999999983,0.476936276204469688098825922983
+0.49999999999999989,0.476936276204469749859690066536
+0.49999999999999994,0.47693627620446981162055421009
+0.50000000000000011,0.47693627620446999690314664075
+0.50000000000000022,0.476936276204470120424874927857
+0.50000000000000033,0.476936276204470243946603214964
+0.50000000000000044,0.476936276204470367468331502071
+0.50000000000000056,0.476936276204470490990059789178
+0.50000000000000067,0.476936276204470614511788076286
+0.50000000000000078,0.476936276204470738033516363393
+0.50000000000000089,0.4769362762044708615552446505
+0.500000000000001,0.476936276204470985076972937607
+0.50000000000000111,0.476936276204471108598701224714
+0.99999999999999889,5.66676502191633181061441214145
+0.999999999999999,5.67591573974471317882020497616
+0.99999999999999911,5.68612844131039098005160412756
+0.99999999999999922,5.69768514275317414947187188334
+0.99999999999999933,5.71099812575505447299174850294
+0.99999999999999944,5.72670523026542455395142564004
+0.99999999999999956,5.74587239219118047033348138881
+0.99999999999999967,5.77049185355333287054782202161
+0.99999999999999978,5.80501868319345330018125827039
+0.99999999999999989,5.86358474875516792720766258583
+
diff --git 
a/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_big_data.csv
 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_big_data.csv
new file mode 100644
index 0000000..a1fb022
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_big_data.csv
@@ -0,0 +1,71 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# (C) Copyright John Maddock 2006-7.
+# Use, modification and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+# Extracted from: boost/libs/math/test/erf_inv_big_data.ipp
+
+0.1040364789689237257780791904879173249974e-287,25.67656975866313564221220720210665888424
+0.2450719147172718682892603854216257656211e-282,25.43473899116627259523415573506406682191
+0.1540635014300999959290867576988859893076e-280,25.35326739382267978364899192426764093558
+0.1611739209855438499046222515484436760851e-273,25.03273169525729329624232590800413859205
+0.2781078901633922872827030068281452871724e-257,24.27512000889987237167959986465966890736
+0.8837820681846044382839818307987545000847e-230,22.93495456229819340317355998004446065241
+0.2845511846316072211634522373099423142668e-217,22.29887672117465208331735160799934601323
+0.662011186385949636511655220013184768187e-212,22.02033498263166080103335807537366542004
+0.1113036584088835972703336205837326051999e-204,21.63966022176108818726656867477709317659
+0.891278753475701910699424577762349195273e-195,21.10677883438545215904960900943812596651
+0.1243375606125932867518669565282002251187e-191,20.93474611896499049089384377937974962862
+0.963481566993839758726177317091390488753e-188,20.72000418866581389344212197908962674052
+0.1401265332225531798315780528328964001988e-167,19.56909048260682534539216053987194710384
+0.7560947579095629715718115752684784187336e-154,18.74494560773490096872968649468667611647
+0.2179858592534951368829782528927445425064e-151,18.59346814776643084908600446605555382182
+0.5208405693419352209961246987938629746382e-131,17.28776816936911830963159975455855394785
+0.5376896865772724579278898126622880802843e-127,17.01882418428649428645068852272615582504
+0.1449951754876351411423239477096839160613e-114,16.15763261758311680195806763260744183267
+0.3189212836315126918184908007667112228414e-108,15.70012576642621709878560027430710336585
+0.1297369886602648502231884634897739722895e-107,15.65546665388935777226018284593032382368
+0.2602821327589283546034644516083144727197e-103,15.33647743152077165227354683273671003413
+0.1590035594081776210615338926776093483999e-96,14.81946110818452708436758045284808001241
+0.2720908074515874075065365442027770927174e-96,14.80136596875698966601900922593357151598
+0.3706750818860597777102228512408751197629e-87,14.07473190298865115531360325271322276549
+0.3489539664837006993042769801460156282803e-83,13.74669364199157990777768669458685265472
+0.1947620269151566018960312643171165602404e-79,13.43009987431500967622734624289673694188
+0.5129832297533069185312436951335270626736e-73,12.86957639029561313119037589037764922829
+0.9272206234111032518003166213125699095688e-70,12.57574048509944911276422466059543556873
+0.1460449453933976163637836336160553873706e-68,12.46599637375040876166599138465603014844
+0.8768204308820258161647995326423963318019e-68,12.39412855962299302779918858089973570208
+0.3176564512338672251415481009303329209995e-61,11.77127316747622053532997731227084711173
+0.1172937774279091349820752991146331290881e-54,11.11297358832626209702547835299261897395
+0.3822700066028246575792241970441049035743e-52,10.85058828114725350216779162566256390403
+0.4793602326185941396320885530096873382082e-41,9.607343723542133339487829511083057069273
+0.106201511009181306109371950926034372112e-40,9.566077632118694585964030456246300276422
+0.1026229395621872573137267432392849554594e-24,7.413111396559360630306454710337113823294
+0.1433834013674021626308791056277965507134e-19,6.574536501026477531602186421374903506051
+0.2287484604245551701383736767135709586086e-12,5.183672646329402197657171322814515766894
+0.4054382118091501052221361988694253843776e-11,4.903978376537783549083010816442468785255
+0.5517250582467272934968166422809728429772e-9,4.386634719241854622420456791837035899963
+0.1708920553446964553659243704866375034024e-5,3.383585107532389209677350898502297644278
+0.2892930668406028955896934416060055101499e-5,3.308042643013470022433136596357284983766
+0.7029998339557698614154475319387477445787e-5,3.17687395056466498458108882620439294841
+0.3973417837559780922949941034264323413971e-4,2.90551585834893682957467771364140403613
+0.04246334897368917726269942070072005435577,1.434684541881674882915735690265484394337
+0.159920364968560744996532371753339418774,0.9937250495458864822841753804433979428041
+0.2425390104583346613758947085681683120129,0.826370276571483139432927467391341431105
+0.7954739362140872788414606986417965117653,0.1832884885283639734589195991287031281603
+0.9236374866673857278562449757419727802699,0.06777816075457032123722158048171767182127
diff --git 
a/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_data.csv
 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_data.csv
new file mode 100644
index 0000000..1c7b2a9
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_data.csv
@@ -0,0 +1,122 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# (C) Copyright John Maddock 2006-7.
+# Use, modification and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+# Extracted from: boost/libs/math/test/erfc_inv_data.ipp
+
+0.00956696830689907073974609375,1.832184391051582711731256541599359331735
+0.063665688037872314453125,1.311339282092737086640055105484822812599
+0.068892158567905426025390625,1.286316565305373898738127895195338854718
+0.071423359215259552001953125,1.274755321776344058215704428086211324587
+0.0728825032711029052734375,1.268242522387371561738393687518023984868
+0.09234277904033660888671875,1.190178756246535875259766567441510867604
+0.102432854473590850830078125,1.154826903977823078146497880706118113588
+0.1942635476589202880859375,0.9178735528443878579995511780412810469667
+0.19508080184459686279296875,0.9161942752629032646404767631869277618212
+0.21972350776195526123046875,0.8678064594007161661713535461829067693456
+0.224929034709930419921875,0.8580919924152284867224297625328485999768
+0.2503655254840850830078125,0.8127923779477598070926819995714374417663
+0.25179326534271240234375,0.8103475955423936417307157186107256388648
+0.2539736330509185791015625,0.8066326381314558738773191719462921998277
+0.27095401287078857421875,0.7784313874823551106598826200232666844639
+0.2837726771831512451171875,0.7579355956263440770864088550908621329508
+0.298227965831756591796875,0.7355614373173595299453301005770428516518
+0.3152261674404144287109375,0.7101588837290742217667270852502075077668
+0.342373371124267578125,0.6713881533266128640126408255047881638389
+0.347730338573455810546875,0.6639738263692763456669198307149427734317
+0.3737452030181884765625,0.6289572925573171740836428308746584439674
+0.37676393985748291015625,0.6249936843093662471116097431474787933967
+0.42041814327239990234375,0.5697131213589784467617578394703976041604
+0.4238486588001251220703125,0.5655171880456876504494070613171955224472
+0.4420680999755859375,0.5435569422159360687847790186563654276103
+0.553845942020416259765625,0.4186121208546731033057205459902879301199
+0.55699646472930908203125,0.4152898953738801984047941692529271391195
+0.59405887126922607421875,0.3768620801611051992528860948080812212023
+0.603826224803924560546875,0.3669220210390825311547962776125822899061
+0.61633408069610595703125,0.3542977152760563782151668726041057557165
+0.63310086727142333984375,0.3375493847053488720470432821496358279516
+0.634198963642120361328125,0.3364591774366710656954166264654945559873
+0.722588002681732177734375,0.2510244067029671790889794981353227476998
+0.763116896152496337890625,0.213115119330839975829499967157244997714
+0.784454047679901123046875,0.1934073617841803428235669261097060281642
+0.797477066516876220703125,0.1814532246720147926398927046057793150106
+0.81746232509613037109375,0.1632073953550647568421821286058243218715
+0.843522548675537109375,0.1395756320903277910768376053314442757507
+0.8441753387451171875,0.1389857795955756484965030151195660030168
+0.87748873233795166015625,0.109002961098867662134935094105847496074
+0.8911724090576171875,0.09674694516640724629590870677194632943569
+0.91597831249237060546875,0.07460044047654119877070700345343119035515
+0.94951736927032470703125,0.04476895818328636312384562686715995170129
+0.970751285552978515625,0.0259268064334840921104659134138093242797
+0.97513782978057861328125,0.02203709146986755832638577823832075055744
+0.97952878475189208984375,0.01814413302702029459097557481591610553903
+0.981178104877471923828125,0.01668201759439857888105181293763417899072
+1.0073254108428955078125,-0.006492067534753215749601670217642082465642
+1.09376299381256103515625,-0.08328747254794857150987333986733043734817
+1.0944411754608154296875,-0.08389270963798942778622198997355058545872
+1.264718532562255859375,-0.2390787735821979528028028789569770109829
+1.27952671051025390625,-0.2530214201700340392837551955289041822603
+1.29262602329254150390625,-0.2654374523135675523971788948011709467352
+1.3109557628631591796875,-0.282950508503826367238408926581528085458
+1.31148135662078857421875,-0.2834552014554130441860525970673030809536
+1.32721102237701416015625,-0.2986277427848421570858990348074985028421
+1.3574702739715576171875,-0.3282140305634627092431945088114761850208
+1.362719058990478515625,-0.3334035993712283467959295804559099468454
+1.3896572589874267578125,-0.3603304982893212173104266596348905175268
+1.4120922088623046875,-0.3831602323665075533579267768785894144888
+1.41872966289520263671875,-0.3899906753567599452444107492361433402154
+1.45167791843414306640625,-0.4244594733907945411184647153213164209335
+1.48129451274871826171875,-0.4563258063707025027210352963461819167707
+1.4862649440765380859375,-0.4617640058971775089811390737537561779898
+1.50937330722808837890625,-0.4874174763856674076219106695373814892182
+1.5154802799224853515625,-0.4943041993872143888987628020569772222018
+1.52750003337860107421875,-0.5079978091910991117615000459548117088362
+1.53103363513946533203125,-0.5120597685873370942783226077302881881069
+1.58441460132598876953125,-0.5756584292527058478710392476034273328569
+1.5879499912261962890625,-0.5800336103175463592377907341030447077804
+1.59039986133575439453125,-0.5830784871670823806198622501806646778319
+1.59455978870391845703125,-0.588273673825686998734497652983815773459
+1.59585726261138916015625,-0.5899005483108011364541949539839185473259
+1.5962116718292236328125,-0.5903454775096607218832535637355431851718
+1.6005609035491943359375,-0.5958247243549040349587326482492767206448
+1.6150619983673095703125,-0.6143583249050861028039832921829036722514
+1.62944734096527099609375,-0.6331707263097125575937994856370309207836
+1.64380657672882080078125,-0.6524069265890823819975498133014027247554
+1.6469156742095947265625,-0.656635855345815020063728463464436343698
+1.67001712322235107421875,-0.6888269167957872563013714303376548038671
+1.6982586383819580078125,-0.7302336318927408409119676651737758401138
+1.74485766887664794921875,-0.8046505193013635090578266413458426260098
+1.75686132907867431640625,-0.8253191678260588578995203396384711816647
+1.81158387660980224609375,-0.9300427626888758122211127950646282789481
+1.826751708984375,-0.9629665092443368464606966822833571908852
+1.83147108554840087890625,-0.9736479209913771931387473923084901789046
+1.84174954891204833984375,-0.997713670556719074960678197806799852186
+1.8679864406585693359375,-1.065050516333636716777334376076374184102
+1.90044414997100830078125,-1.164612422633086435501625591693259387477
+1.91433393955230712890625,-1.215315881176612875682446995412738776976
+1.91501367092132568359375,-1.217962731073139868794942852653058932976
+1.918984889984130859375,-1.233778505900771488542027767896521427575
+1.92977702617645263671875,-1.28019542575660930623179572273596558907
+1.93538987636566162109375,-1.306695301483797253764522033930388453334
+1.93773555755615234375,-1.318335478463913327121670503572736587296
+1.94118559360504150390625,-1.33613349872692113073358883961598631154
+1.96221935749053955078125,-1.468821071545234761861756248744372345584
+1.98576259613037109375,-1.733272259459038694476413373595347034928
+1.9881370067596435546875,-1.77921769652839903464038407684397479173
+1.99292266368865966796875,-1.904368122482929779094714951471938518496
diff --git 
a/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_limit_data.csv
 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_limit_data.csv
new file mode 100644
index 0000000..e11d63b
--- /dev/null
+++ 
b/commons-numbers-gamma/src/test/resources/org/apache/commons/numbers/gamma/erfc_inv_limit_data.csv
@@ -0,0 +1,126 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Erfc limits: [2, 0]
+# Test the critical regions of the range using x -> 2, x -> 1 and x -> 0.
+#
+# Data generated using the following matlab script:
+#
+# f = fopen('erfc_inv_data2.csv', 'wt');
+#
+# % Uses variable precision arithmetic (vpa) to exactly approach 2, 1, and 0.
+# digits(30);
+# x = [1:10];
+# % anonymous function writes the argument and the inverse erfc function value 
to file
+# pr = @(p) fprintf(f, "%.17g,%s\n", p, vpa(erfcinv(p)));
+# for i = x
+#   pr(sym(2) - sym(i*2^-52));
+# end
+# for i = flip(x)
+#   pr(sym(1) + sym(i*2^-52));
+# end
+# for i = x
+#   pr(sym(1) - sym(i*2^-53));
+# end
+# for i = [1022:1074]
+#   pr(sym(2^-i));
+# end
+#
+# clear pr;
+# fclose(f);
+
+1.9999999999999998,-5.80501868319345330018125827039
+1.9999999999999996,-5.74587239219118047033348138881
+1.9999999999999993,-5.71099812575505447299174850294
+1.9999999999999991,-5.68612844131039098005160412756
+1.9999999999999989,-5.66676502191633181061441214145
+1.9999999999999987,-5.65089613274800609228436271922
+1.9999999999999984,-5.63744534527960023748265465926
+1.9999999999999982,-5.62576850990669864999311406674
+1.999999999999998,-5.61544924946836459402473243698
+1.9999999999999978,-5.60620273614591880997628748599
+1.0000000000000022,-0.00000000000000196781907536082825960007390103
+1.000000000000002,-0.00000000000000177103716782474543364006651093
+1.0000000000000018,-0.00000000000000157425526028866260768005912082
+1.0000000000000016,-0.00000000000000137747335275257978172005173072
+1.0000000000000013,-0.00000000000000118069144521649695576004434062
+1.0000000000000011,-0.000000000000000983909537680414129800036950514
+1.0000000000000009,-0.000000000000000787127630144331303840029560411
+1.0000000000000007,-0.000000000000000590345722608248477880022170308
+1.0000000000000004,-0.000000000000000393563815072165651920014780205
+1.0000000000000002,-0.000000000000000196781907536082825960007390103
+0.99999999999999989,0.0000000000000000983909537680414129800036950513
+0.99999999999999978,0.000000000000000196781907536082825960007390103
+0.99999999999999967,0.000000000000000295172861304124238940011085154
+0.99999999999999956,0.000000000000000393563815072165651920014780205
+0.99999999999999944,0.000000000000000491954768840207064900018475257
+0.99999999999999933,0.000000000000000590345722608248477880022170308
+0.99999999999999922,0.00000000000000068873667637628989086002586536
+0.99999999999999911,0.000000000000000787127630144331303840029560411
+0.999999999999999,0.000000000000000885518583912372716820033255462
+0.99999999999999889,0.000000000000000983909537680414129800036950514
+2.2250738585072014e-308,26.5432584542509813824702197142
+1.1125369292536007e-308,26.5563029415321441679153405313
+5.5626846462680035e-309,26.5693410335354631524963692932
+2.7813423231340017e-309,26.5823727396534961059275476332
+1.3906711615670009e-309,26.5953980692558387594638242111
+6.9533558078350043e-310,26.6084170316892033030771306907
+3.4766779039175022e-310,26.6214296362774965380042408218
+1.7383389519587511e-310,26.6344358923218976865134392447
+8.6916947597937554e-311,26.6474358091009358607255378148
+4.3458473798968777e-311,26.6604293958705671923131736698
+2.1729236899484389e-311,26.6734166618642516248908042397
+1.0864618449742194e-311,26.6863976162930293708963792246
+5.4323092248710971e-312,26.699372268345597034754317556
+2.7161546124355486e-312,26.7123406271883834040981478159
+1.3580773062177743e-312,26.7253027019656249108199828507
+6.7903865310888714e-313,26.7382585017994407637028926996
+3.3951932655444357e-313,26.7512080357899077543812138002
+1.6975966327722179e-313,26.7641513130151347383628860778
+8.4879831638610893e-314,26.7770883425313367928370423088
+4.2439915819305446e-314,26.7900191333729090529792854372
+2.1219957909652723e-314,26.8029436945525002284563786595
+1.0609978954826362e-314,26.815862035061085801821439453
+5.3049894774131808e-315,26.8287741638680409104801716724
+2.6524947387065904e-315,26.8416800899212129138981887478
+1.3262473693532952e-315,26.8545798221469936477090752797
+6.631236846766476e-316,26.8674733694503913663725033119
+3.315618423383238e-316,26.8803607407151023760214626828
+1.657809211691619e-316,26.8932419448035823591274814881
+8.289046058458095e-317,26.9061169905571173926026022553
+4.1445230292290475e-317,26.9189858867958946609468413249
+2.0722615146145237e-317,26.9318486423190728660398925827
+1.0361307573072619e-317,26.9447052659048523351659414994
+5.1806537865363094e-318,26.9575557663105448288506308428
+2.5903268932681547e-318,26.9704001522726430500794648573
+1.2951634466340773e-318,26.9832384325068898564572535936
+6.4758172331703867e-319,26.9960706157083471768585828536
+3.2379086165851934e-319,27.0088967105514646341097473482
+1.6189543082925967e-319,27.0217167256901478752331045806
+8.0947715414629834e-320,27.0345306697578266107753941403
+4.0473857707314917e-320,27.0473385513675223647322209666
+2.0236928853657458e-320,27.0601403791119159365716211893
+1.0118464426828729e-320,27.0729361615634145768504148458
+5.0592322134143646e-321,27.0857259072742188779079005788
+2.5296161067071823e-321,27.0985096247763893811123628229
+1.2648080533535912e-321,27.1112873225819129021268414685
+6.3240402667679558e-322,27.1240590091827685756516570398
+3.1620201333839779e-322,27.1368246930509936210922905338
+1.5810100666919889e-322,27.1495843826387488305923857306
+7.9050503334599447e-323,27.1623380863783837808628725138
+3.9525251667299724e-323,27.175085812682501770229502028
+1.9762625833649862e-323,27.1878275699440244823124378705
+9.8813129168249309e-324,27.2005633665362563777429614682
+4.9406564584124654e-324,27.2132932108129488153138238647
diff --git a/pom.xml b/pom.xml
index 2cf2e8a..cbc990d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,6 +156,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.commons</groupId>
+        <artifactId>commons-numbers-gamma</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.commons</groupId>
         <artifactId>commons-numbers-arrays</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/src/main/resources/checkstyle/checkstyle-suppressions.xml 
b/src/main/resources/checkstyle/checkstyle-suppressions.xml
index 4bb2be9..4937eac 100644
--- a/src/main/resources/checkstyle/checkstyle-suppressions.xml
+++ b/src/main/resources/checkstyle/checkstyle-suppressions.xml
@@ -24,6 +24,10 @@
   <suppress checks="FileLengthCheck" files=".*[/\\]Complex(Test)?\.java" />
   <suppress checks="FileLengthCheck" 
files=".*jmh[/\\]core[/\\]LinearCombinations.java" />
   <suppress checks="MethodLength" files=".*[/\\]Complex\.java" />
+  <!-- Naming and method logic preserved from the original Boost C++ code -->
+  <suppress checks="MethodLength" files=".*[/\\]BoostErf\.java" />
+  <suppress checks="LocalVariableName" files=".*[/\\]BoostErf\.java" />
+  <suppress checks="LocalFinalVariableName" files=".*[/\\]BoostErf\.java" />
   <!-- Method "hashCode()" is defined in the "Angle" parent class; subclasses 
have no additional fields to include in the hash. -->
   <suppress checks="EqualsHashCode" files=".*[/\\]Angle\.java" />
 
diff --git a/src/main/resources/pmd/pmd-ruleset.xml 
b/src/main/resources/pmd/pmd-ruleset.xml
index d86342e..4ba91b2 100644
--- a/src/main/resources/pmd/pmd-ruleset.xml
+++ b/src/main/resources/pmd/pmd-ruleset.xml
@@ -83,6 +83,13 @@
       <property name="utilityClassPattern" value="[A-Z][a-zA-Z0-9]*" />
     </properties>
   </rule>
+  <rule ref="category/java/codestyle.xml/LocalVariableNamingConventions">
+    <properties>
+      <!-- Naming conventions preserved from the original Boost C++ code -->
+      <property name="violationSuppressXPath"
+        value="//ClassOrInterfaceDeclaration[@SimpleName='BoostErf']"/>
+    </properties>
+  </rule>
 
   <rule ref="category/java/design.xml/CyclomaticComplexity">
     <properties>
@@ -122,7 +129,8 @@
                or @SimpleName='LogGammaSum'
                or @SimpleName='InverseErf'
                or @SimpleName='InvGamma1pm1'
-               or @SimpleName='RegularizedBeta']"/>
+               or @SimpleName='RegularizedBeta'
+               or @SimpleName='BoostErf']"/>
     </properties>
   </rule>
 
@@ -141,7 +149,8 @@
       <property name="violationSuppressXPath"
         value="//ClassOrInterfaceDeclaration[@SimpleName='Complex'
                or @SimpleName='BigFraction'
-               or @SimpleName='Fraction']"/>
+               or @SimpleName='Fraction'
+               or @SimpleName='BoostErf']"/>
     </properties>
   </rule>
 

Reply via email to