[MATH-996] Fix creation of Fraction/BigFraction objects in maxDenominator mode when the value is close to an actual fraction.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1519204 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d7fd760eb8
commit
86545dab3e
3
pom.xml
3
pom.xml
|
@ -132,6 +132,9 @@
|
||||||
<contributor>
|
<contributor>
|
||||||
<name>Eldar Agalarov</name>
|
<name>Eldar Agalarov</name>
|
||||||
</contributor>
|
</contributor>
|
||||||
|
<contributor>
|
||||||
|
<name>Tim Allison</name>
|
||||||
|
</contributor>
|
||||||
<contributor>
|
<contributor>
|
||||||
<name>C. Scott Ananian</name>
|
<name>C. Scott Ananian</name>
|
||||||
</contributor>
|
</contributor>
|
||||||
|
|
|
@ -51,6 +51,11 @@ If the output is not quite correct, check for invisible trailing spaces!
|
||||||
</properties>
|
</properties>
|
||||||
<body>
|
<body>
|
||||||
<release version="x.y" date="TBD" description="TBD">
|
<release version="x.y" date="TBD" description="TBD">
|
||||||
|
<action dev="tn" type=fix issue="MATH-996" due-to="Tim Allison">
|
||||||
|
Creating a "Fraction" or "BigFraction" object with a maxDenominator parameter
|
||||||
|
does not throw a "FractionConversionException" in case the value is very close
|
||||||
|
to fraction.
|
||||||
|
</action>
|
||||||
<action dev="tn" type="add" issue="MATH-1028" due-to="Thorsten Schäfer">
|
<action dev="tn" type="add" issue="MATH-1028" due-to="Thorsten Schäfer">
|
||||||
Added new distance metric "EarthMoversDistance".
|
Added new distance metric "EarthMoversDistance".
|
||||||
</action>
|
</action>
|
||||||
|
|
|
@ -301,6 +301,11 @@ public class BigFraction
|
||||||
p2 = (a1 * p1) + p0;
|
p2 = (a1 * p1) + p0;
|
||||||
q2 = (a1 * q1) + q0;
|
q2 = (a1 * q1) + q0;
|
||||||
if ((p2 > overflow) || (q2 > overflow)) {
|
if ((p2 > overflow) || (q2 > overflow)) {
|
||||||
|
// in maxDenominator mode, if the last fraction was very close to the actual value
|
||||||
|
// q2 may overflow in the next iteration; in this case return the last one.
|
||||||
|
if (epsilon == 0.0 && FastMath.abs(q1) < maxDenominator) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
throw new FractionConversionException(value, p2, q2);
|
throw new FractionConversionException(value, p2, q2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,9 @@ public class Fraction
|
||||||
/** Serializable version identifier */
|
/** Serializable version identifier */
|
||||||
private static final long serialVersionUID = 3698073679419233275L;
|
private static final long serialVersionUID = 3698073679419233275L;
|
||||||
|
|
||||||
|
/** The default epsilon used for convergence. */
|
||||||
|
private static final double DEFAULT_EPSILON = 1e-5;
|
||||||
|
|
||||||
/** The denominator. */
|
/** The denominator. */
|
||||||
private final int denominator;
|
private final int denominator;
|
||||||
|
|
||||||
|
@ -96,7 +99,7 @@ public class Fraction
|
||||||
* converge.
|
* converge.
|
||||||
*/
|
*/
|
||||||
public Fraction(double value) throws FractionConversionException {
|
public Fraction(double value) throws FractionConversionException {
|
||||||
this(value, 1.0e-5, 100);
|
this(value, DEFAULT_EPSILON, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,8 +185,7 @@ public class Fraction
|
||||||
throw new FractionConversionException(value, a0, 1l);
|
throw new FractionConversionException(value, a0, 1l);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for (almost) integer arguments, which should not go
|
// check for (almost) integer arguments, which should not go to iterations.
|
||||||
// to iterations.
|
|
||||||
if (FastMath.abs(a0 - value) < epsilon) {
|
if (FastMath.abs(a0 - value) < epsilon) {
|
||||||
this.numerator = (int) a0;
|
this.numerator = (int) a0;
|
||||||
this.denominator = 1;
|
this.denominator = 1;
|
||||||
|
@ -206,7 +208,13 @@ public class Fraction
|
||||||
long a1 = (long)FastMath.floor(r1);
|
long a1 = (long)FastMath.floor(r1);
|
||||||
p2 = (a1 * p1) + p0;
|
p2 = (a1 * p1) + p0;
|
||||||
q2 = (a1 * q1) + q0;
|
q2 = (a1 * q1) + q0;
|
||||||
|
|
||||||
if ((FastMath.abs(p2) > overflow) || (FastMath.abs(q2) > overflow)) {
|
if ((FastMath.abs(p2) > overflow) || (FastMath.abs(q2) > overflow)) {
|
||||||
|
// in maxDenominator mode, if the last fraction was very close to the actual value
|
||||||
|
// q2 may overflow in the next iteration; in this case return the last one.
|
||||||
|
if (epsilon == 0.0 && FastMath.abs(q1) < maxDenominator) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
throw new FractionConversionException(value, p2, q2);
|
throw new FractionConversionException(value, p2, q2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,9 @@ public class BigFractionTest {
|
||||||
assertFraction(8, 13, new BigFraction(0.6152, 99));
|
assertFraction(8, 13, new BigFraction(0.6152, 99));
|
||||||
assertFraction(510, 829, new BigFraction(0.6152, 999));
|
assertFraction(510, 829, new BigFraction(0.6152, 999));
|
||||||
assertFraction(769, 1250, new BigFraction(0.6152, 9999));
|
assertFraction(769, 1250, new BigFraction(0.6152, 9999));
|
||||||
|
|
||||||
|
// MATH-996
|
||||||
|
assertFraction(1, 2, new BigFraction(0.5000000001, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -129,6 +129,9 @@ public class FractionTest {
|
||||||
assertFraction(8, 13, new Fraction(0.6152, 99));
|
assertFraction(8, 13, new Fraction(0.6152, 99));
|
||||||
assertFraction(510, 829, new Fraction(0.6152, 999));
|
assertFraction(510, 829, new Fraction(0.6152, 999));
|
||||||
assertFraction(769, 1250, new Fraction(0.6152, 9999));
|
assertFraction(769, 1250, new Fraction(0.6152, 9999));
|
||||||
|
|
||||||
|
// MATH-996
|
||||||
|
assertFraction(1, 2, new Fraction(0.5000000001, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -141,7 +144,9 @@ public class FractionTest {
|
||||||
|
|
||||||
private void checkIntegerOverflow(double a) {
|
private void checkIntegerOverflow(double a) {
|
||||||
try {
|
try {
|
||||||
new Fraction(a, 1.0e-12, 1000);
|
@SuppressWarnings("unused")
|
||||||
|
Fraction f = new Fraction(a, 1.0e-12, 1000);
|
||||||
|
//System.out.println(f.getNumerator() + "/" + f.getDenominator());
|
||||||
Assert.fail("an exception should have been thrown");
|
Assert.fail("an exception should have been thrown");
|
||||||
} catch (ConvergenceException ce) {
|
} catch (ConvergenceException ce) {
|
||||||
// expected behavior
|
// expected behavior
|
||||||
|
|
Loading…
Reference in New Issue