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.

JIRA: MATH-414

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1040471 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Phil Steitz 2010-11-30 11:55:22 +00:00
parent 0615bf8dc0
commit 5fe9b36c45
3 changed files with 26 additions and 17 deletions

View File

@ -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))));
}
/**

View File

@ -118,6 +118,13 @@ The <action> type attribute can be add,update,fix,remove.
</action>
</release>
<release version="2.2" date="TBD" description="TBD">
<action dev="psteitz" type="fix" issue="MATH-414">
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.
</action>
<action dev="psteitz" type="update" issue="MATH-420">
Added toString() override to StatisticalSummaryValues.
</action>

View File

@ -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 {