commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From l..@apache.org
Subject svn commit: r615856 - in /commons/proper/math/trunk/src: java/org/apache/commons/math/fraction/Fraction.java test/org/apache/commons/math/fraction/FractionTest.java
Date Mon, 28 Jan 2008 11:58:05 GMT
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 {@link #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 {@link #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);



Mime
View raw message