MATH-1396: Overflow.

This commit is contained in:
Gilles 2016-11-17 19:06:06 +01:00
parent 7b42d43fa0
commit af1b5872ab
2 changed files with 39 additions and 8 deletions

View File

@ -22,10 +22,8 @@ import org.apache.commons.math4.exception.util.LocalizedFormats;
import org.apache.commons.rng.UniformRandomProvider; import org.apache.commons.rng.UniformRandomProvider;
/** /**
* Implementation of the uniform integer distribution. * Implementation of the <a href="http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)">
* * uniform integer distribution</a>.
* @see <a href="http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)"
* >Uniform distribution (discrete), at Wikipedia</a>
* *
* @since 3.0 * @since 3.0
*/ */
@ -36,6 +34,10 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
private final int lower; private final int lower;
/** Upper bound (inclusive) of this distribution. */ /** Upper bound (inclusive) of this distribution. */
private final int upper; private final int upper;
/** "upper" + "lower" (to avoid overflow). */
private final double upperPlusLower;
/** "upper" - "lower" (to avoid overflow). */
private final double upperMinusLower;
/** /**
* Creates a new uniform integer distribution using the given lower and * Creates a new uniform integer distribution using the given lower and
@ -55,6 +57,8 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
} }
this.lower = lower; this.lower = lower;
this.upper = upper; this.upper = upper;
upperPlusLower = (double) upper + (double) lower;
upperMinusLower = (double) upper - (double) lower;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -63,7 +67,7 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
if (x < lower || x > upper) { if (x < lower || x > upper) {
return 0; return 0;
} }
return 1.0 / (upper - lower + 1); return 1.0 / (upperMinusLower + 1);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -75,7 +79,7 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
if (x > upper) { if (x > upper) {
return 1; return 1;
} }
return (x - lower + 1.0) / (upper - lower + 1.0); return (x - lower + 1.0) / (upperMinusLower + 1.0);
} }
/** /**
@ -86,7 +90,7 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
*/ */
@Override @Override
public double getNumericalMean() { public double getNumericalMean() {
return 0.5 * (lower + upper); return 0.5 * upperPlusLower;
} }
/** /**
@ -97,7 +101,7 @@ public class UniformIntegerDistribution extends AbstractIntegerDistribution {
*/ */
@Override @Override
public double getNumericalVariance() { public double getNumericalVariance() {
double n = upper - lower + 1; double n = upperMinusLower + 1;
return (n * n - 1) / 12.0; return (n * n - 1) / 12.0;
} }

View File

@ -22,6 +22,7 @@ import org.junit.Test;
import org.apache.commons.math4.distribution.IntegerDistribution; import org.apache.commons.math4.distribution.IntegerDistribution;
import org.apache.commons.math4.distribution.UniformIntegerDistribution; import org.apache.commons.math4.distribution.UniformIntegerDistribution;
import org.apache.commons.math4.exception.NumberIsTooLargeException; import org.apache.commons.math4.exception.NumberIsTooLargeException;
import org.apache.commons.math4.util.Precision;
/** /**
* Test cases for UniformIntegerDistribution. See class javadoc for * Test cases for UniformIntegerDistribution. See class javadoc for
@ -112,4 +113,30 @@ public class UniformIntegerDistributionTest extends IntegerDistributionAbstractT
// Degenerate case is allowed. // Degenerate case is allowed.
new UniformIntegerDistribution(0, 0); new UniformIntegerDistribution(0, 0);
} }
// MATH-1396
@Test
public void testLargeRangeSubtractionOverflow() {
final int hi = Integer.MAX_VALUE / 2 + 10;
UniformIntegerDistribution dist = new UniformIntegerDistribution(-hi, hi - 1);
final double tol = Math.ulp(1d);
Assert.assertEquals(0.5 / hi, dist.probability(123456), tol);
Assert.assertEquals(0.5, dist.cumulativeProbability(-1), tol);
Assert.assertTrue(Precision.equals((Math.pow(2d * hi, 2) - 1) / 12, dist.getNumericalVariance(), 1));
}
// MATH-1396
@Test
public void testLargeRangeAdditionOverflow() {
final int hi = Integer.MAX_VALUE / 2 + 10;
UniformIntegerDistribution dist = new UniformIntegerDistribution(hi - 1, hi + 1);
final double tol = Math.ulp(1d);
Assert.assertEquals(1d / 3d, dist.probability(hi), tol);
Assert.assertEquals(2d / 3d, dist.cumulativeProbability(hi), tol);
Assert.assertTrue(Precision.equals(hi, dist.getNumericalMean(), 1));
}
} }