added checks for integer overflows during double to Fraction conversion

JIRA: MATH-182

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@617482 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2008-02-01 12:02:12 +00:00
parent 630d9afa3c
commit 9fec47b79b
5 changed files with 59 additions and 20 deletions

View File

@ -71,6 +71,8 @@ public class MessagesResources_fr
// org.apache.commons.math.fraction.FractionConversionException
{ "Unable to convert {0} to fraction after {1} iterations",
"Impossible de convertir {0} en fraction apr\u00e8s {1} it\u00e9rations" },
{ "Overflow trying to convert {0} to fraction ({1}/{2})",
"D\u00e9passement de capacit\u00e9 lors de la conversion de {0} en fraction ({1}/{2})" },
// org.apache.commons.math.analysis.UnivariateRealSolverUtils
{ "Number of iterations={0}, maximum iterations={1}, initial={2}, lower bound={3}, upper bound={4}," +

View File

@ -34,7 +34,7 @@ public class Fraction extends Number implements Comparable {
public static final Fraction ZERO = new Fraction(0, 1);
/** Serializable version identifier */
private static final long serialVersionUID = 5463066929751300926L;
private static final long serialVersionUID = -8958519416450949235L;
/** The denominator. */
private int denominator;
@ -128,33 +128,40 @@ public class Fraction extends Number implements Comparable {
private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
throws FractionConversionException
{
long overflow = Integer.MAX_VALUE;
double r0 = value;
int a0 = (int)Math.floor(r0);
long a0 = (long)Math.floor(r0);
if (a0 > overflow) {
throw new FractionConversionException(value, a0, 1l);
}
// check for (almost) integer arguments, which should not go
// to iterations.
if (Math.abs(a0 - value) < epsilon) {
this.numerator = a0;
this.numerator = (int) a0;
this.denominator = 1;
return;
}
int p0 = 1;
int q0 = 0;
int p1 = a0;
int q1 = 1;
int p2 = 0;
int q2 = 1;
long p0 = 1;
long q0 = 0;
long p1 = a0;
long q1 = 1;
long p2 = 0;
long q2 = 1;
int n = 0;
boolean stop = false;
do {
++n;
double r1 = 1.0 / (r0 - a0);
int a1 = (int)Math.floor(r1);
long a1 = (long)Math.floor(r1);
p2 = (a1 * p1) + p0;
q2 = (a1 * q1) + q0;
if ((p2 > overflow) || (q2 > overflow)) {
throw new FractionConversionException(value, p2, q2);
}
double convergent = (double)p2 / (double)q2;
if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
@ -174,11 +181,11 @@ public class Fraction extends Number implements Comparable {
}
if (q2 < maxDenominator) {
this.numerator = p2;
this.denominator = q2;
this.numerator = (int) p2;
this.denominator = (int) q2;
} else {
this.numerator = p1;
this.denominator = q1;
this.numerator = (int) p1;
this.denominator = (int) q1;
}
}

View File

@ -17,7 +17,7 @@
package org.apache.commons.math.fraction;
import org.apache.commons.math.MaxIterationsExceededException;
import org.apache.commons.math.ConvergenceException;
/**
* Error thrown when a double value cannot be converted to a fraction
@ -25,10 +25,10 @@ import org.apache.commons.math.MaxIterationsExceededException;
*
* @version $Revision$ $Date$
*/
public class FractionConversionException extends MaxIterationsExceededException {
public class FractionConversionException extends ConvergenceException {
/** Serializable version identifier. */
private static final long serialVersionUID = 4588659344016668813L;
private static final long serialVersionUID = -4661812640132576263L;
/**
* Constructs an exception with specified formatted detail message.
@ -37,9 +37,20 @@ public class FractionConversionException extends MaxIterationsExceededException
* @param maxIterations maximal number of iterations allowed
*/
public FractionConversionException(double value, int maxIterations) {
super(maxIterations,
"Unable to convert {0} to fraction after {1} iterations",
super("Unable to convert {0} to fraction after {1} iterations",
new Object[] { new Double(value), new Integer(maxIterations) });
}
/**
* Constructs an exception with specified formatted detail message.
* Message formatting is delegated to {@link java.text.MessageFormat}.
* @param value double value to convert
* @param p current numerator
* @param q current denominator
*/
public FractionConversionException(double value, long p, long q) {
super("Overflow trying to convert {0} to fraction ({1}/{2})",
new Object[] { new Double(value), new Long(p), new Long(q) });
}
}

View File

@ -133,6 +133,22 @@ public class FractionTest extends TestCase {
assertFraction(769, 1250, new Fraction(0.6152, 9999));
}
public void testIntegerOverflow() {
checkIntegerOverflow(0.75000000001455192);
checkIntegerOverflow(1.0e10);
}
private void checkIntegerOverflow(double a) {
try {
new Fraction(a, 1.0e-12, 1000);
fail("an exception should have been thrown");
} catch (ConvergenceException ce) {
// expected behavior
} catch (Exception e) {
fail("wrong exception caught");
}
}
public void testEpsilonLimitConstructor() throws ConvergenceException {
assertFraction(2, 5, new Fraction(0.4, 1.0e-5, 100));

View File

@ -136,6 +136,9 @@ Commons Math Release Notes</title>
<action dev="luc" type="update" issue="MATH-181" due-to="Niall Pemberton">
Add Fraction constructor using max denominator value.
</action>
<action dev="luc" type="fix" issue="MATH-18x">
Add integer overflow checks in Fraction constructor using double parameter.
</action>
</release>
<release version="1.1" date="2005-12-17"
description="This is a maintenance release containing bug fixes and enhancements.