View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math.fraction;
18  
19  import java.math.BigInteger;
20  import org.apache.commons.math.util.MathUtils;
21  
22  /**
23   * Representation of a rational number.
24   *
25   * @since 1.1
26   * @version $Revision: 620373 $ $Date: 2008-02-10 18:18:39 -0700 (Sun, 10 Feb 2008) $
27   */
28  public class Fraction extends Number implements Comparable {
29  
30      /** A fraction representing "1 / 1". */
31      public static final Fraction ONE = new Fraction(1, 1);
32  
33      /** A fraction representing "0 / 1". */
34      public static final Fraction ZERO = new Fraction(0, 1);
35  
36      /** Serializable version identifier */
37      private static final long serialVersionUID = -8958519416450949235L;
38      
39      /** The denominator. */
40      private final int denominator;
41      
42      /** The numerator. */
43      private final int numerator;
44  
45      /**
46       * Create a fraction given the double value.
47       * @param value the double value to convert to a fraction.
48       * @throws FractionConversionException if the continued fraction failed to
49       *         converge.
50       */
51      public Fraction(double value) throws FractionConversionException {
52          this(value, 1.0e-5, 100);
53      }
54  
55      /**
56       * Create a fraction given the double value and maximum error allowed.
57       * <p>
58       * References:
59       * <ul>
60       * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
61       * Continued Fraction</a> equations (11) and (22)-(26)</li>
62       * </ul>
63       * </p>
64       * @param value the double value to convert to a fraction.
65       * @param epsilon maximum error allowed.  The resulting fraction is within
66       *        <code>epsilon</code> of <code>value</code>, in absolute terms.
67       * @param maxIterations maximum number of convergents
68       * @throws FractionConversionException if the continued fraction failed to
69       *         converge.
70       */
71      public Fraction(double value, double epsilon, int maxIterations)
72          throws FractionConversionException
73      {
74          this(value, epsilon, Integer.MAX_VALUE, maxIterations);
75      }
76  
77      /**
78       * Create a fraction given the double value and maximum denominator.
79       * <p>
80       * References:
81       * <ul>
82       * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
83       * Continued Fraction</a> equations (11) and (22)-(26)</li>
84       * </ul>
85       * </p>
86       * @param value the double value to convert to a fraction.
87       * @param maxDenominator The maximum allowed value for denominator
88       * @throws FractionConversionException if the continued fraction failed to
89       *         converge
90       */
91      public Fraction(double value, int maxDenominator)
92          throws FractionConversionException
93      {
94         this(value, 0, maxDenominator, 100);
95      }
96  
97      /**
98       * Create a fraction given the double value and either the maximum error
99       * allowed or the maximum number of denominator digits.
100      * <p>
101      *
102      * NOTE: This constructor is called with EITHER
103      *   - a valid epsilon value and the maxDenominator set to Integer.MAX_VALUE
104      *     (that way the maxDenominator has no effect).
105      * OR
106      *   - a valid maxDenominator value and the epsilon value set to zero
107      *     (that way epsilon only has effect if there is an exact match before
108      *     the maxDenominator value is reached).
109      * </p><p>
110      *
111      * It has been done this way so that the same code can be (re)used for both
112      * scenarios. However this could be confusing to users if it were part of
113      * the public API and this constructor should therefore remain PRIVATE.
114      * </p>
115      *
116      * See JIRA issue ticket MATH-181 for more details:
117      *
118      *     https://issues.apache.org/jira/browse/MATH-181
119      *
120      * @param value the double value to convert to a fraction.
121      * @param epsilon maximum error allowed.  The resulting fraction is within
122      *        <code>epsilon</code> of <code>value</code>, in absolute terms.
123      * @param maxDenominator maximum denominator value allowed.
124      * @param maxIterations maximum number of convergents
125      * @throws FractionConversionException if the continued fraction failed to
126      *         converge.
127      */
128     private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
129         throws FractionConversionException
130     {
131         long overflow = Integer.MAX_VALUE;
132         double r0 = value;
133         long a0 = (long)Math.floor(r0);
134         if (a0 > overflow) {
135             throw new FractionConversionException(value, a0, 1l);
136         }
137 
138         // check for (almost) integer arguments, which should not go
139         // to iterations.
140         if (Math.abs(a0 - value) < epsilon) {
141             this.numerator = (int) a0;
142             this.denominator = 1;
143             return;
144         }
145 
146        long p0 = 1;
147         long q0 = 0;
148         long p1 = a0;
149         long q1 = 1;
150 
151         long p2 = 0;
152         long q2 = 1;
153 
154         int n = 0;
155         boolean stop = false;
156         do {
157             ++n;
158             double r1 = 1.0 / (r0 - a0);
159             long a1 = (long)Math.floor(r1);
160             p2 = (a1 * p1) + p0;
161             q2 = (a1 * q1) + q0;
162             if ((p2 > overflow) || (q2 > overflow)) {
163                 throw new FractionConversionException(value, p2, q2);
164             }
165             
166             double convergent = (double)p2 / (double)q2;
167             if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
168                 p0 = p1;
169                 p1 = p2;
170                 q0 = q1;
171                 q1 = q2;
172                 a0 = a1;
173                 r0 = r1;
174             } else {
175                 stop = true;
176             }
177         } while (!stop);
178 
179         if (n >= maxIterations) {
180             throw new FractionConversionException(value, maxIterations);
181         }
182         
183         if (q2 < maxDenominator) {
184             this.numerator = (int) p2;
185             this.denominator = (int) q2;
186         } else {
187             this.numerator = (int) p1;
188             this.denominator = (int) q1;
189         }
190 
191     }
192     
193     /**
194      * Create a fraction given the numerator and denominator.  The fraction is
195      * reduced to lowest terms.
196      * @param num the numerator.
197      * @param den the denominator.
198      * @throws ArithmeticException if the denomiator is <code>zero</code>
199      */
200     public Fraction(int num, int den) {
201         super();
202         if (den == 0) {
203             throw new ArithmeticException("The denominator must not be zero");
204         }
205         if (den < 0) {
206             if (num == Integer.MIN_VALUE ||
207                     den == Integer.MIN_VALUE) {
208                 throw new ArithmeticException("overflow: can't negate");
209             }
210             num = -num;
211             den = -den;
212         }
213         // reduce numerator and denominator by greatest common denominator.
214         int d = MathUtils.gcd(num, den);
215         if (d > 1) {
216             num /= d;
217             den /= d;
218         }
219         
220         // move sign to numerator.
221         if (den < 0) {
222             num *= -1;
223             den *= -1;
224         }
225         this.numerator = num;
226         this.denominator = den;
227     }
228     
229     /**
230      * Returns the absolute value of this fraction.
231      * @return the absolute value.
232      */
233     public Fraction abs() {
234         Fraction ret;
235         if (numerator >= 0) {
236             ret = this;
237         } else {
238             ret = negate();
239         }
240         return ret;        
241     }
242     
243     /**
244      * Compares this object to another based on size.
245      * @param object the object to compare to
246      * @return -1 if this is less than <tt>object</tt>, +1 if this is greater
247      *         than <tt>object</tt>, 0 if they are equal.
248      */
249     public int compareTo(Object object) {
250         int ret = 0;
251         
252         if (this != object) { 
253             Fraction other = (Fraction)object;
254             double first = doubleValue();
255             double second = other.doubleValue();
256             
257             if (first < second) {
258                 ret = -1;
259             } else if (first > second) {
260                 ret = 1;
261             }
262         }
263         
264         return ret;
265     }
266     
267     /**
268      * Gets the fraction as a <tt>double</tt>. This calculates the fraction as
269      * the numerator divided by denominator.
270      * @return the fraction as a <tt>double</tt>
271      */
272     public double doubleValue() {
273         return (double)numerator / (double)denominator;
274     }
275     
276     /**
277      * Test for the equality of two fractions.  If the lowest term
278      * numerator and denominators are the same for both fractions, the two
279      * fractions are considered to be equal.
280      * @param other fraction to test for equality to this fraction
281      * @return true if two fractions are equal, false if object is
282      *         <tt>null</tt>, not an instance of {@link Fraction}, or not equal
283      *         to this fraction instance.
284      */
285     public boolean equals(Object other) {
286         boolean ret;
287         
288         if (this == other) { 
289             ret = true;
290         } else if (other == null) {
291             ret = false;
292         } else {
293             try {
294                 // since fractions are always in lowest terms, numerators and
295                 // denominators can be compared directly for equality.
296                 Fraction rhs = (Fraction)other;
297                 ret = (numerator == rhs.numerator) &&
298                     (denominator == rhs.denominator);
299             } catch (ClassCastException ex) {
300                 // ignore exception
301                 ret = false;
302             }
303         }
304         
305         return ret;
306     }
307     
308     /**
309      * Gets the fraction as a <tt>float</tt>. This calculates the fraction as
310      * the numerator divided by denominator.
311      * @return the fraction as a <tt>float</tt>
312      */
313     public float floatValue() {
314         return (float)doubleValue();
315     }
316     
317     /**
318      * Access the denominator.
319      * @return the denominator.
320      */
321     public int getDenominator() {
322         return denominator;
323     }
324     
325     /**
326      * Access the numerator.
327      * @return the numerator.
328      */
329     public int getNumerator() {
330         return numerator;
331     }
332     
333     /**
334      * Gets a hashCode for the fraction.
335      * @return a hash code value for this object
336      */
337     public int hashCode() {
338         return 37 * (37 * 17 + getNumerator()) + getDenominator();
339     }
340     
341     /**
342      * Gets the fraction as an <tt>int</tt>. This returns the whole number part
343      * of the fraction.
344      * @return the whole number fraction part
345      */
346     public int intValue() {
347         return (int)doubleValue();
348     }
349     
350     /**
351      * Gets the fraction as a <tt>long</tt>. This returns the whole number part
352      * of the fraction.
353      * @return the whole number fraction part
354      */
355     public long longValue() {
356         return (long)doubleValue();
357     }
358     
359     /**
360      * Return the additive inverse of this fraction.
361      * @return the negation of this fraction.
362      */
363     public Fraction negate() {
364         if (numerator==Integer.MIN_VALUE) {
365             throw new ArithmeticException("overflow: too large to negate");
366         }
367         return new Fraction(-numerator, denominator);
368     }
369 
370     /**
371      * Return the multiplicative inverse of this fraction.
372      * @return the reciprocal fraction
373      */
374     public Fraction reciprocal() {
375         return new Fraction(denominator, numerator);
376     }
377     
378     /**
379      * <p>Adds the value of this fraction to another, returning the result in reduced form.
380      * The algorithm follows Knuth, 4.5.1.</p>
381      *
382      * @param fraction  the fraction to add, must not be <code>null</code>
383      * @return a <code>Fraction</code> instance with the resulting values
384      * @throws IllegalArgumentException if the fraction is <code>null</code>
385      * @throws ArithmeticException if the resulting numerator or denominator exceeds
386      *  <code>Integer.MAX_VALUE</code>
387      */
388     public Fraction add(Fraction fraction) {
389         return addSub(fraction, true /* add */);
390     }
391 
392     /**
393      * <p>Subtracts the value of another fraction from the value of this one, 
394      * returning the result in reduced form.</p>
395      *
396      * @param fraction  the fraction to subtract, must not be <code>null</code>
397      * @return a <code>Fraction</code> instance with the resulting values
398      * @throws IllegalArgumentException if the fraction is <code>null</code>
399      * @throws ArithmeticException if the resulting numerator or denominator
400      *   cannot be represented in an <code>int</code>.
401      */
402     public Fraction subtract(Fraction fraction) {
403         return addSub(fraction, false /* subtract */);
404     }
405 
406     /** 
407      * Implement add and subtract using algorithm described in Knuth 4.5.1.
408      * 
409      * @param fraction the fraction to subtract, must not be <code>null</code>
410      * @param isAdd true to add, false to subtract
411      * @return a <code>Fraction</code> instance with the resulting values
412      * @throws IllegalArgumentException if the fraction is <code>null</code>
413      * @throws ArithmeticException if the resulting numerator or denominator
414      *   cannot be represented in an <code>int</code>.
415      */
416     private Fraction addSub(Fraction fraction, boolean isAdd) {
417         if (fraction == null) {
418             throw new IllegalArgumentException("The fraction must not be null");
419         }
420         // zero is identity for addition.
421         if (numerator == 0) {
422             return isAdd ? fraction : fraction.negate();
423         }
424         if (fraction.numerator == 0) {
425             return this;
426         }     
427         // if denominators are randomly distributed, d1 will be 1 about 61%
428         // of the time.
429         int d1 = MathUtils.gcd(denominator, fraction.denominator);
430         if (d1==1) {
431             // result is ( (u*v' +/- u'v) / u'v')
432             int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator);
433             int upv = MathUtils.mulAndCheck(fraction.numerator, denominator);
434             return new Fraction
435                 (isAdd ? MathUtils.addAndCheck(uvp, upv) : 
436                  MathUtils.subAndCheck(uvp, upv),
437                  MathUtils.mulAndCheck(denominator, fraction.denominator));
438         }
439         // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
440         // exercise 7.  we're going to use a BigInteger.
441         // t = u(v'/d1) +/- v(u'/d1)
442         BigInteger uvp = BigInteger.valueOf(numerator)
443         .multiply(BigInteger.valueOf(fraction.denominator/d1));
444         BigInteger upv = BigInteger.valueOf(fraction.numerator)
445         .multiply(BigInteger.valueOf(denominator/d1));
446         BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
447         // but d2 doesn't need extra precision because
448         // d2 = gcd(t,d1) = gcd(t mod d1, d1)
449         int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
450         int d2 = (tmodd1==0)?d1:MathUtils.gcd(tmodd1, d1);
451 
452         // result is (t/d2) / (u'/d1)(v'/d2)
453         BigInteger w = t.divide(BigInteger.valueOf(d2));
454         if (w.bitLength() > 31) {
455             throw new ArithmeticException
456             ("overflow: numerator too large after multiply");
457         }
458         return new Fraction (w.intValue(), 
459                 MathUtils.mulAndCheck(denominator/d1, 
460                         fraction.denominator/d2));
461     }
462 
463     /**
464      * <p>Multiplies the value of this fraction by another, returning the 
465      * result in reduced form.</p>
466      *
467      * @param fraction  the fraction to multiply by, must not be <code>null</code>
468      * @return a <code>Fraction</code> instance with the resulting values
469      * @throws IllegalArgumentException if the fraction is <code>null</code>
470      * @throws ArithmeticException if the resulting numerator or denominator exceeds
471      *  <code>Integer.MAX_VALUE</code>
472      */
473     public Fraction multiply(Fraction fraction) {
474         if (fraction == null) {
475             throw new IllegalArgumentException("The fraction must not be null");
476         }
477         if (numerator == 0 || fraction.numerator == 0) {
478             return ZERO;
479         }
480         // knuth 4.5.1
481         // make sure we don't overflow unless the result *must* overflow.
482         int d1 = MathUtils.gcd(numerator, fraction.denominator);
483         int d2 = MathUtils.gcd(fraction.numerator, denominator);
484         return getReducedFraction
485         (MathUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
486                 MathUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
487     }
488 
489     /**
490      * <p>Divide the value of this fraction by another.</p>
491      *
492      * @param fraction  the fraction to divide by, must not be <code>null</code>
493      * @return a <code>Fraction</code> instance with the resulting values
494      * @throws IllegalArgumentException if the fraction is <code>null</code>
495      * @throws ArithmeticException if the fraction to divide by is zero
496      * @throws ArithmeticException if the resulting numerator or denominator exceeds
497      *  <code>Integer.MAX_VALUE</code>
498      */
499     public Fraction divide(Fraction fraction) {
500         if (fraction == null) {
501             throw new IllegalArgumentException("The fraction must not be null");
502         }
503         if (fraction.numerator == 0) {
504             throw new ArithmeticException("The fraction to divide by must not be zero");
505         }
506         return multiply(fraction.reciprocal());
507     }
508     
509     /**
510      * <p>Creates a <code>Fraction</code> instance with the 2 parts
511      * of a fraction Y/Z.</p>
512      *
513      * <p>Any negative signs are resolved to be on the numerator.</p>
514      *
515      * @param numerator  the numerator, for example the three in 'three sevenths'
516      * @param denominator  the denominator, for example the seven in 'three sevenths'
517      * @return a new fraction instance, with the numerator and denominator reduced
518      * @throws ArithmeticException if the denominator is <code>zero</code>
519      */
520     public static Fraction getReducedFraction(int numerator, int denominator) {
521         if (denominator == 0) {
522             throw new ArithmeticException("The denominator must not be zero");
523         }
524         if (numerator==0) {
525             return ZERO; // normalize zero.
526         }
527         // allow 2^k/-2^31 as a valid fraction (where k>0)
528         if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
529             numerator/=2; denominator/=2;
530         }
531         if (denominator < 0) {
532             if (numerator==Integer.MIN_VALUE ||
533                     denominator==Integer.MIN_VALUE) {
534                 throw new ArithmeticException("overflow: can't negate");
535             }
536             numerator = -numerator;
537             denominator = -denominator;
538         }
539         // simplify fraction.
540         int gcd = MathUtils.gcd(numerator, denominator);
541         numerator /= gcd;
542         denominator /= gcd;
543         return new Fraction(numerator, denominator);
544     }
545 }