diff --git a/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java index 456555bf3..0d7ce7da3 100644 --- a/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java +++ b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java @@ -114,26 +114,20 @@ public class NormalDistributionImpl extends AbstractContinuousDistribution /** * For this distribution, {@code X}, this method returns {@code P(X < x)}. + * If {@code x}is more than 40 standard deviations from the mean, 0 or 1 is returned, + * as in these cases the actual value is within {@code Double.MIN_VALUE} of 0 or 1. * * @param x Value at which the CDF is evaluated. * @return CDF evaluated at {@code x}. - * @throws MathException if the algorithm fails to converge; unless - * {@code x} is more than 20 standard deviations from the mean, in which - * case the convergence exception is caught and 0 or 1 is returned. + * @throws MathException if the algorithm fails to converge */ public double cumulativeProbability(double x) throws MathException { - try { - return 0.5 * (1.0 + Erf.erf((x - mean) / - (standardDeviation * FastMath.sqrt(2.0)))); - } catch (MaxIterationsExceededException ex) { - if (x < (mean - 20 * standardDeviation)) { // JDK 1.5 blows at 38 - return 0; - } else if (x > (mean + 20 * standardDeviation)) { - return 1; - } else { - throw ex; - } + final double dev = x - mean; + if (FastMath.abs(dev) > 40 * standardDeviation) { + return dev < 0 ? 0.0d : 1.0d; } + return 0.5 * (1.0 + Erf.erf((dev) / + (standardDeviation * FastMath.sqrt(2.0)))); } /** diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 339854ddd..0c4519b03 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -118,6 +118,13 @@ The type attribute can be add,update,fix,remove. + + Modified NormalDistributionImpl.cumulativeProbability to return 0 or 1, + respectively for values more than 40 standard deviations from the mean. + For these values, the actual probability is indistinguishable from 0 or 1 + as a double. Top coding improves performance for extreme values and prevents + convergence exceptions. + Added toString() override to StatisticalSummaryValues. diff --git a/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java b/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java index e9ffe5c1e..f8ebebf68 100644 --- a/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java +++ b/src/test/java/org/apache/commons/math/distribution/NormalDistributionTest.java @@ -152,14 +152,16 @@ public class NormalDistributionTest extends ContinuousDistributionAbstractTest /** * Check to make sure top-coding of extreme values works correctly. - * Verifies fix for JIRA MATH-167 + * Verifies fixes for JIRA MATH-167, MATH-414 */ public void testExtremeValues() throws Exception { NormalDistribution distribution = new NormalDistributionImpl(0, 1); - for (int i = 0; i < 100; i+=5) { // make sure no convergence exception + for (int i = 0; i < 100; i++) { // make sure no convergence exception double lowerTail = distribution.cumulativeProbability(-i); double upperTail = distribution.cumulativeProbability(i); - if (i < 10) { // make sure not top-coded + if (i < 9) { // make sure not top-coded + // For i = 10, due to bad tail precision in erf (MATH-364), 1 is returned + // TODO: once MATH-364 is resolved, replace 9 with 30 assertTrue(lowerTail > 0.0d); assertTrue(upperTail < 1.0d); } @@ -168,6 +170,12 @@ public class NormalDistributionTest extends ContinuousDistributionAbstractTest assertTrue(upperTail > 0.99999); } } + + assertEquals(distribution.cumulativeProbability(Double.MAX_VALUE), 1, 0); + assertEquals(distribution.cumulativeProbability(-Double.MAX_VALUE), 0, 0); + assertEquals(distribution.cumulativeProbability(Double.POSITIVE_INFINITY), 1, 0); + assertEquals(distribution.cumulativeProbability(Double.NEGATIVE_INFINITY), 0, 0); + } public void testMath280() throws MathException {