Author: luc
Date: Mon Jan 28 03:57:55 2008
New Revision: 615856
URL: http://svn.apache.org/viewvc?rev=615856&view=rev
Log:
added new constructors for Fractions
JIRA: MATH-181
Modified:
commons/proper/math/trunk/src/java/org/apache/commons/math/fraction/Fraction.java
commons/proper/math/trunk/src/test/org/apache/commons/math/fraction/FractionTest.java
Modified:
commons/proper/math/trunk/src/java/org/apache/commons/math/fraction/Fraction.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/java/org/apache/commons/math/fraction/Fraction.java?rev=615856&r1=615855&r2=615856&view=diff
==============================================================================
---
commons/proper/math/trunk/src/java/org/apache/commons/math/fraction/Fraction.java
(original)
+++
commons/proper/math/trunk/src/java/org/apache/commons/math/fraction/Fraction.java
Mon Jan 28 03:57:55 2008
@@ -32,10 +32,23 @@
/** A fraction representing "0 / 1". */
public static final Fraction ZERO = new Fraction(0, 1);
+
+ /**
+ * The maximal number of denominator digits that can be requested for
double to fraction
+ * conversion.
+ * <p>
+ * When <code>d</code> digits are requested, an integer threshold is
+ * initialized with the value 10<sup>d</sup>. Therefore, <code>d</code>
+ * cannot be larger than this constant. Since the java language uses 32
bits
+ * signed integers, the value for this constant is 9.
+ * </p>
+ *
+ * @see #Fraction(double,int)
+ */
+ public static final int MAX_DENOMINATOR_DIGITS = 9;
/** Serializable version identifier */
- private static final long serialVersionUID = 6222990762865980424L;
-
+ private static final long serialVersionUID = 5463066929751300926L;
/** The denominator. */
private int denominator;
@@ -54,7 +67,7 @@
}
/**
- * Create a fraction given the double value.
+ * Create a fraction given the double value and maximum error allowed.
* <p>
* References:
* <ul>
@@ -72,6 +85,82 @@
public Fraction(double value, double epsilon, int maxIterations)
throws FractionConversionException
{
+ this(value, epsilon, Integer.MAX_VALUE, maxIterations);
+ }
+
+ /**
+ * Convert a number of denominator digits to a denominator max value.
+ * @param denominatorDigits The maximum number of denominator digits.
+ * @return the maximal value for denominator
+ * @throws IllegalArgumentException if more than [EMAIL PROTECTED]
#MAX_DENOMINATOR_DIGITS}
+ * are requested
+ */
+ private static int maxDenominator(int denominatorDigits)
+ throws IllegalArgumentException
+ {
+ if (denominatorDigits > MAX_DENOMINATOR_DIGITS) {
+ throw new IllegalArgumentException("too many digits requested");
+ }
+ return (int)Math.pow(10, denominatorDigits);
+ }
+
+ /**
+ * Create a fraction given the double value and maximum number of
+ * denominator digits.
+ * <p>
+ * References:
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
+ * Continued Fraction</a> equations (11) and (22)-(26)</li>
+ * </ul>
+ * </p>
+ * @param value the double value to convert to a fraction.
+ * @param denominatorDigits The maximum number of denominator digits.
+ * @throws FractionConversionException if the continued fraction failed to
+ * converge
+ * @throws IllegalArgumentException if more than [EMAIL PROTECTED]
#MAX_DENOMINATOR_DIGITS}
+ * are requested
+ */
+ public Fraction(double value, int denominatorDigits)
+ throws FractionConversionException, IllegalArgumentException
+ {
+ this(value, 0, maxDenominator(denominatorDigits), 100);
+ }
+
+ /**
+ * Create a fraction given the double value and either the maximum error
+ * allowed or the maximum number of denominator digits.
+ * <p>
+ *
+ * NOTE: This constructor is called with EITHER
+ * - a valid epsilon value and the maxDenominator set to
Integer.MAX_VALUE
+ * (that way the maxDenominator has no effect).
+ * OR
+ * - a valid maxDenominator value and the epsilon value set to zero
+ * (that way epsilon only has effect if there is an exact match before
+ * the maxDenominator value is reached).
+ * <p>
+ *
+ * It has been done this way so that the same code can be (re)used for both
+ * scenarios. However this could be confusing to users if it were part of
+ * the public API and this constructor should therefore remain PRIVATE.
+ * </p>
+ *
+ * See JIRA issue ticket MATH-181 for more details:
+ *
+ * https://issues.apache.org/jira/browse/MATH-181
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within
+ * <code>epsilon</code> of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws FractionConversionException if the continued fraction failed to
+ * converge.
+ */
+ private Fraction(double value, double epsilon, int maxDenominator, int
maxIterations)
+ throws FractionConversionException
+ {
double r0 = value;
int a0 = (int)Math.floor(r0);
@@ -101,7 +190,7 @@
q2 = (a1 * q1) + q0;
double convergent = (double)p2 / (double)q2;
- if (n < maxIterations && Math.abs(convergent - value) > epsilon) {
+ if (n < maxIterations && Math.abs(convergent - value) > epsilon &&
q2 < maxDenominator) {
p0 = p1;
p1 = p2;
q0 = q1;
@@ -117,8 +206,13 @@
throw new FractionConversionException(value, maxIterations);
}
- this.numerator = p2;
- this.denominator = q2;
+ if (q2 < maxDenominator) {
+ this.numerator = p2;
+ this.denominator = q2;
+ } else {
+ this.numerator = p1;
+ this.denominator = q1;
+ }
reduce();
}
Modified:
commons/proper/math/trunk/src/test/org/apache/commons/math/fraction/FractionTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/org/apache/commons/math/fraction/FractionTest.java?rev=615856&r1=615855&r2=615856&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/org/apache/commons/math/fraction/FractionTest.java
(original)
+++
commons/proper/math/trunk/src/test/org/apache/commons/math/fraction/FractionTest.java
Mon Jan 28 03:57:55 2008
@@ -121,6 +121,38 @@
assertFraction(10, 11, new Fraction((double)10 / (double)11));
}
+ // MATH-181
+ public void testDigitLimitConstructor() throws ConvergenceException {
+ assertFraction(2, 5, new Fraction(0.4, 1));
+ assertFraction(2, 5, new Fraction(0.4, 2));
+ assertFraction(2, 5, new Fraction(0.4, 3));
+
+ assertFraction(3, 5, new Fraction(0.6152, 1));
+ assertFraction(8, 13, new Fraction(0.6152, 2));
+ assertFraction(510, 829, new Fraction(0.6152, 3));
+ assertFraction(769, 1250, new Fraction(0.6152, 4));
+
+ try {
+ new Fraction(0.6152, 15);
+ fail("an exception should have been thrown");
+ } catch (IllegalArgumentException iae) {
+ // 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));
+
+ assertFraction(3, 5, new Fraction(0.6152, 0.02, 100));
+ assertFraction(8, 13, new Fraction(0.6152, 1.0e-3, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-4, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-5, 100));
+ assertFraction(510, 829, new Fraction(0.6152, 1.0e-6, 100));
+ assertFraction(769, 1250, new Fraction(0.6152, 1.0e-7, 100));
+ }
+
public void testCompareTo() {
Fraction first = new Fraction(1, 2);
Fraction second = new Fraction(1, 3);