Added and used a specialized exception for continued fraction convergence errors

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@506591 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2007-02-12 19:23:17 +00:00
parent ee71801e77
commit 21a95478c2
2 changed files with 507 additions and 486 deletions

View File

@ -1,486 +1,485 @@
/* /*
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 * The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with * (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.commons.math.fraction; package org.apache.commons.math.fraction;
import java.math.BigInteger; import java.math.BigInteger;
import org.apache.commons.math.ConvergenceException; import org.apache.commons.math.util.MathUtils;
import org.apache.commons.math.util.MathUtils;
/**
/** * Representation of a rational number.
* Representation of a rational number. *
* * @since 1.1
* @since 1.1 * @version $Revision$ $Date$
* @version $Revision$ $Date$ */
*/ public class Fraction extends Number implements Comparable {
public class Fraction extends Number implements Comparable {
/** A fraction representing "1 / 1". */
/** A fraction representing "1 / 1". */ public static final Fraction ONE = new Fraction(1, 1);
public static final Fraction ONE = new Fraction(1, 1);
/** A fraction representing "0 / 1". */
/** A fraction representing "0 / 1". */ public static final Fraction ZERO = new Fraction(0, 1);
public static final Fraction ZERO = new Fraction(0, 1);
/** Serializable version identifier */
/** Serializable version identifier */ private static final long serialVersionUID = 6222990762865980424L;
private static final long serialVersionUID = 65382027393090L;
/** The denominator. */ /** The denominator. */
private int denominator; private int denominator;
/** The numerator. */ /** The numerator. */
private int numerator; private int numerator;
/** /**
* Create a fraction given the double value. * Create a fraction given the double value.
* @param value the double value to convert to a fraction. * @param value the double value to convert to a fraction.
* @throws ConvergenceException if the continued fraction failed to * @throws FractionConversionException if the continued fraction failed to
* converge. * converge.
*/ */
public Fraction(double value) throws ConvergenceException { public Fraction(double value) throws FractionConversionException {
this(value, 1.0e-5, 100); this(value, 1.0e-5, 100);
} }
/** /**
* Create a fraction given the double value. * Create a fraction given the double value.
* <p> * <p>
* References: * References:
* <ul> * <ul>
* <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html"> * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
* Continued Fraction</a> equations (11) and (22)-(26)</li> * Continued Fraction</a> equations (11) and (22)-(26)</li>
* </ul> * </ul>
* </p> * </p>
* @param value the double value to convert to a fraction. * @param value the double value to convert to a fraction.
* @param epsilon maximum error allowed. The resulting fraction is within * @param epsilon maximum error allowed. The resulting fraction is within
* <code>epsilon</code> of <code>value</code>, in absolute terms. * <code>epsilon</code> of <code>value</code>, in absolute terms.
* @param maxIterations maximum number of convergents * @param maxIterations maximum number of convergents
* @throws ConvergenceException if the continued fraction failed to * @throws FractionConversionException if the continued fraction failed to
* converge. * converge.
*/ */
public Fraction(double value, double epsilon, int maxIterations) public Fraction(double value, double epsilon, int maxIterations)
throws ConvergenceException throws FractionConversionException
{ {
double r0 = value; double r0 = value;
int a0 = (int)Math.floor(r0); int a0 = (int)Math.floor(r0);
// check for (almost) integer arguments, which should not go // check for (almost) integer arguments, which should not go
// to iterations. // to iterations.
if (Math.abs(a0 - value) < epsilon) { if (Math.abs(a0 - value) < epsilon) {
this.numerator = a0; this.numerator = a0;
this.denominator = 1; this.denominator = 1;
return; return;
} }
int p0 = 1; int p0 = 1;
int q0 = 0; int q0 = 0;
int p1 = a0; int p1 = a0;
int q1 = 1; int q1 = 1;
int p2 = 0; int p2 = 0;
int q2 = 1; int q2 = 1;
int n = 0; int n = 0;
boolean stop = false; boolean stop = false;
do { do {
++n; ++n;
double r1 = 1.0 / (r0 - a0); double r1 = 1.0 / (r0 - a0);
int a1 = (int)Math.floor(r1); int a1 = (int)Math.floor(r1);
p2 = (a1 * p1) + p0; p2 = (a1 * p1) + p0;
q2 = (a1 * q1) + q0; q2 = (a1 * q1) + q0;
double convergent = (double)p2 / (double)q2; double convergent = (double)p2 / (double)q2;
if (n < maxIterations && Math.abs(convergent - value) > epsilon) { if (n < maxIterations && Math.abs(convergent - value) > epsilon) {
p0 = p1; p0 = p1;
p1 = p2; p1 = p2;
q0 = q1; q0 = q1;
q1 = q2; q1 = q2;
a0 = a1; a0 = a1;
r0 = r1; r0 = r1;
} else { } else {
stop = true; stop = true;
} }
} while (!stop); } while (!stop);
if (n >= maxIterations) { if (n >= maxIterations) {
throw new ConvergenceException( throw new FractionConversionException(value, maxIterations);
"Unable to convert double to fraction"); }
}
this.numerator = p2;
this.numerator = p2; this.denominator = q2;
this.denominator = q2; reduce();
reduce(); }
}
/**
/** * Create a fraction given the numerator and denominator. The fraction is
* Create a fraction given the numerator and denominator. The fraction is * reduced to lowest terms.
* reduced to lowest terms. * @param num the numerator.
* @param num the numerator. * @param den the denominator.
* @param den the denominator. * @throws ArithmeticException if the denomiator is <code>zero</code>
* @throws ArithmeticException if the denomiator is <code>zero</code> */
*/ public Fraction(int num, int den) {
public Fraction(int num, int den) { super();
super(); if (den == 0) {
if (den == 0) { throw new ArithmeticException("The denominator must not be zero");
throw new ArithmeticException("The denominator must not be zero"); }
} if (den < 0) {
if (den < 0) { if (num == Integer.MIN_VALUE ||
if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) {
den == Integer.MIN_VALUE) { throw new ArithmeticException("overflow: can't negate");
throw new ArithmeticException("overflow: can't negate"); }
} num = -num;
num = -num; den = -den;
den = -den; }
} this.numerator = num;
this.numerator = num; this.denominator = den;
this.denominator = den; reduce();
reduce(); }
}
/**
/** * Returns the absolute value of this fraction.
* Returns the absolute value of this fraction. * @return the absolute value.
* @return the absolute value. */
*/ public Fraction abs() {
public Fraction abs() { Fraction ret;
Fraction ret; if (numerator >= 0) {
if (numerator >= 0) { ret = this;
ret = this; } else {
} else { ret = negate();
ret = negate(); }
} return ret;
return ret; }
}
/**
/** * Compares this object to another based on size.
* Compares this object to another based on size. * @param object the object to compare to
* @param object the object to compare to * @return -1 if this is less than <tt>object</tt>, +1 if this is greater
* @return -1 if this is less than <tt>object</tt>, +1 if this is greater * than <tt>object</tt>, 0 if they are equal.
* than <tt>object</tt>, 0 if they are equal. */
*/ public int compareTo(Object object) {
public int compareTo(Object object) { int ret = 0;
int ret = 0;
if (this != object) {
if (this != object) { Fraction other = (Fraction)object;
Fraction other = (Fraction)object; double first = doubleValue();
double first = doubleValue(); double second = other.doubleValue();
double second = other.doubleValue();
if (first < second) {
if (first < second) { ret = -1;
ret = -1; } else if (first > second) {
} else if (first > second) { ret = 1;
ret = 1; }
} }
}
return ret;
return ret; }
}
/**
/** * Gets the fraction as a <tt>double</tt>. This calculates the fraction as
* Gets the fraction as a <tt>double</tt>. This calculates the fraction as * the numerator divided by denominator.
* the numerator divided by denominator. * @return the fraction as a <tt>double</tt>
* @return the fraction as a <tt>double</tt> */
*/ public double doubleValue() {
public double doubleValue() { return (double)numerator / (double)denominator;
return (double)numerator / (double)denominator; }
}
/**
/** * Test for the equality of two fractions. If the lowest term
* Test for the equality of two fractions. If the lowest term * numerator and denominators are the same for both fractions, the two
* numerator and denominators are the same for both fractions, the two * fractions are considered to be equal.
* fractions are considered to be equal. * @param other fraction to test for equality to this fraction
* @param other fraction to test for equality to this fraction * @return true if two fractions are equal, false if object is
* @return true if two fractions are equal, false if object is * <tt>null</tt>, not an instance of {@link Fraction}, or not equal
* <tt>null</tt>, not an instance of {@link Fraction}, or not equal * to this fraction instance.
* to this fraction instance. */
*/ public boolean equals(Object other) {
public boolean equals(Object other) { boolean ret;
boolean ret;
if (this == other) {
if (this == other) { ret = true;
ret = true; } else if (other == null) {
} else if (other == null) { ret = false;
ret = false; } else {
} else { try {
try { // since fractions are always in lowest terms, numerators and
// since fractions are always in lowest terms, numerators and // denominators can be compared directly for equality.
// denominators can be compared directly for equality. Fraction rhs = (Fraction)other;
Fraction rhs = (Fraction)other; ret = (numerator == rhs.numerator) &&
ret = (numerator == rhs.numerator) && (denominator == rhs.denominator);
(denominator == rhs.denominator); } catch (ClassCastException ex) {
} catch (ClassCastException ex) { // ignore exception
// ignore exception ret = false;
ret = false; }
} }
}
return ret;
return ret; }
}
/**
/** * Gets the fraction as a <tt>float</tt>. This calculates the fraction as
* Gets the fraction as a <tt>float</tt>. This calculates the fraction as * the numerator divided by denominator.
* the numerator divided by denominator. * @return the fraction as a <tt>float</tt>
* @return the fraction as a <tt>float</tt> */
*/ public float floatValue() {
public float floatValue() { return (float)doubleValue();
return (float)doubleValue(); }
}
/**
/** * Access the denominator.
* Access the denominator. * @return the denominator.
* @return the denominator. */
*/ public int getDenominator() {
public int getDenominator() { return denominator;
return denominator; }
}
/**
/** * Access the numerator.
* Access the numerator. * @return the numerator.
* @return the numerator. */
*/ public int getNumerator() {
public int getNumerator() { return numerator;
return numerator; }
}
/**
/** * Gets a hashCode for the fraction.
* Gets a hashCode for the fraction. * @return a hash code value for this object
* @return a hash code value for this object */
*/ public int hashCode() {
public int hashCode() { return 37 * (37 * 17 + getNumerator()) + getDenominator();
return 37 * (37 * 17 + getNumerator()) + getDenominator(); }
}
/**
/** * Gets the fraction as an <tt>int</tt>. This returns the whole number part
* Gets the fraction as an <tt>int</tt>. This returns the whole number part * of the fraction.
* of the fraction. * @return the whole number fraction part
* @return the whole number fraction part */
*/ public int intValue() {
public int intValue() { return (int)doubleValue();
return (int)doubleValue(); }
}
/**
/** * Gets the fraction as a <tt>long</tt>. This returns the whole number part
* Gets the fraction as a <tt>long</tt>. This returns the whole number part * of the fraction.
* of the fraction. * @return the whole number fraction part
* @return the whole number fraction part */
*/ public long longValue() {
public long longValue() { return (long)doubleValue();
return (long)doubleValue(); }
}
/**
/** * Return the additive inverse of this fraction.
* Return the additive inverse of this fraction. * @return the negation of this fraction.
* @return the negation of this fraction. */
*/ public Fraction negate() {
public Fraction negate() { if (numerator==Integer.MIN_VALUE) {
if (numerator==Integer.MIN_VALUE) { throw new ArithmeticException("overflow: too large to negate");
throw new ArithmeticException("overflow: too large to negate"); }
} return new Fraction(-numerator, denominator);
return new Fraction(-numerator, denominator); }
}
/**
/** * Return the multiplicative inverse of this fraction.
* Return the multiplicative inverse of this fraction. * @return the reciprocal fraction
* @return the reciprocal fraction */
*/ public Fraction reciprocal() {
public Fraction reciprocal() { return new Fraction(denominator, numerator);
return new Fraction(denominator, numerator); }
}
/**
/** * <p>Adds the value of this fraction to another, returning the result in reduced form.
* <p>Adds the value of this fraction to another, returning the result in reduced form. * The algorithm follows Knuth, 4.5.1.</p>
* The algorithm follows Knuth, 4.5.1.</p> *
* * @param fraction the fraction to add, must not be <code>null</code>
* @param fraction the fraction to add, must not be <code>null</code> * @return a <code>Fraction</code> instance with the resulting values
* @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code>
* @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator exceeds
* @throws ArithmeticException if the resulting numerator or denominator exceeds * <code>Integer.MAX_VALUE</code>
* <code>Integer.MAX_VALUE</code> */
*/ public Fraction add(Fraction fraction) {
public Fraction add(Fraction fraction) { return addSub(fraction, true /* add */);
return addSub(fraction, true /* add */); }
}
/**
/** * <p>Subtracts the value of another fraction from the value of this one,
* <p>Subtracts the value of another fraction from the value of this one, * returning the result in reduced form.</p>
* returning the result in reduced form.</p> *
* * @param fraction the fraction to subtract, must not be <code>null</code>
* @param fraction the fraction to subtract, must not be <code>null</code> * @return a <code>Fraction</code> instance with the resulting values
* @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code>
* @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator
* @throws ArithmeticException if the resulting numerator or denominator * cannot be represented in an <code>int</code>.
* cannot be represented in an <code>int</code>. */
*/ public Fraction subtract(Fraction fraction) {
public Fraction subtract(Fraction fraction) { return addSub(fraction, false /* subtract */);
return addSub(fraction, false /* subtract */); }
}
/**
/** * Implement add and subtract using algorithm described in Knuth 4.5.1.
* Implement add and subtract using algorithm described in Knuth 4.5.1. *
* * @param fraction the fraction to subtract, must not be <code>null</code>
* @param fraction the fraction to subtract, must not be <code>null</code> * @param isAdd true to add, false to subtract
* @param isAdd true to add, false to subtract * @return a <code>Fraction</code> instance with the resulting values
* @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code>
* @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator
* @throws ArithmeticException if the resulting numerator or denominator * cannot be represented in an <code>int</code>.
* cannot be represented in an <code>int</code>. */
*/ private Fraction addSub(Fraction fraction, boolean isAdd) {
private Fraction addSub(Fraction fraction, boolean isAdd) { if (fraction == null) {
if (fraction == null) { throw new IllegalArgumentException("The fraction must not be null");
throw new IllegalArgumentException("The fraction must not be null"); }
} // zero is identity for addition.
// zero is identity for addition. if (numerator == 0) {
if (numerator == 0) { return isAdd ? fraction : fraction.negate();
return isAdd ? fraction : fraction.negate(); }
} if (fraction.numerator == 0) {
if (fraction.numerator == 0) { return this;
return this; }
} // if denominators are randomly distributed, d1 will be 1 about 61%
// if denominators are randomly distributed, d1 will be 1 about 61% // of the time.
// of the time. int d1 = MathUtils.gcd(denominator, fraction.denominator);
int d1 = MathUtils.gcd(denominator, fraction.denominator); if (d1==1) {
if (d1==1) { // result is ( (u*v' +/- u'v) / u'v')
// result is ( (u*v' +/- u'v) / u'v') int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator);
int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator); int upv = MathUtils.mulAndCheck(fraction.numerator, denominator);
int upv = MathUtils.mulAndCheck(fraction.numerator, denominator); return new Fraction
return new Fraction (isAdd ? MathUtils.addAndCheck(uvp, upv) :
(isAdd ? MathUtils.addAndCheck(uvp, upv) : MathUtils.subAndCheck(uvp, upv),
MathUtils.subAndCheck(uvp, upv), MathUtils.mulAndCheck(denominator, fraction.denominator));
MathUtils.mulAndCheck(denominator, fraction.denominator)); }
} // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
// the quantity 't' requires 65 bits of precision; see knuth 4.5.1 // exercise 7. we're going to use a BigInteger.
// exercise 7. we're going to use a BigInteger. // t = u(v'/d1) +/- v(u'/d1)
// t = u(v'/d1) +/- v(u'/d1) BigInteger uvp = BigInteger.valueOf(numerator)
BigInteger uvp = BigInteger.valueOf(numerator) .multiply(BigInteger.valueOf(fraction.denominator/d1));
.multiply(BigInteger.valueOf(fraction.denominator/d1)); BigInteger upv = BigInteger.valueOf(fraction.numerator)
BigInteger upv = BigInteger.valueOf(fraction.numerator) .multiply(BigInteger.valueOf(denominator/d1));
.multiply(BigInteger.valueOf(denominator/d1)); BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); // but d2 doesn't need extra precision because
// but d2 doesn't need extra precision because // d2 = gcd(t,d1) = gcd(t mod d1, d1)
// d2 = gcd(t,d1) = gcd(t mod d1, d1) int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); int d2 = (tmodd1==0)?d1:MathUtils.gcd(tmodd1, d1);
int d2 = (tmodd1==0)?d1:MathUtils.gcd(tmodd1, d1);
// result is (t/d2) / (u'/d1)(v'/d2)
// result is (t/d2) / (u'/d1)(v'/d2) BigInteger w = t.divide(BigInteger.valueOf(d2));
BigInteger w = t.divide(BigInteger.valueOf(d2)); if (w.bitLength() > 31) {
if (w.bitLength() > 31) { throw new ArithmeticException
throw new ArithmeticException ("overflow: numerator too large after multiply");
("overflow: numerator too large after multiply"); }
} return new Fraction (w.intValue(),
return new Fraction (w.intValue(), MathUtils.mulAndCheck(denominator/d1,
MathUtils.mulAndCheck(denominator/d1, fraction.denominator/d2));
fraction.denominator/d2)); }
}
/**
/** * <p>Multiplies the value of this fraction by another, returning the
* <p>Multiplies the value of this fraction by another, returning the * result in reduced form.</p>
* result in reduced form.</p> *
* * @param fraction the fraction to multiply by, must not be <code>null</code>
* @param fraction the fraction to multiply by, must not be <code>null</code> * @return a <code>Fraction</code> instance with the resulting values
* @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code>
* @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator exceeds
* @throws ArithmeticException if the resulting numerator or denominator exceeds * <code>Integer.MAX_VALUE</code>
* <code>Integer.MAX_VALUE</code> */
*/ public Fraction multiply(Fraction fraction) {
public Fraction multiply(Fraction fraction) { if (fraction == null) {
if (fraction == null) { throw new IllegalArgumentException("The fraction must not be null");
throw new IllegalArgumentException("The fraction must not be null"); }
} if (numerator == 0 || fraction.numerator == 0) {
if (numerator == 0 || fraction.numerator == 0) { return ZERO;
return ZERO; }
} // knuth 4.5.1
// knuth 4.5.1 // make sure we don't overflow unless the result *must* overflow.
// make sure we don't overflow unless the result *must* overflow. int d1 = MathUtils.gcd(numerator, fraction.denominator);
int d1 = MathUtils.gcd(numerator, fraction.denominator); int d2 = MathUtils.gcd(fraction.numerator, denominator);
int d2 = MathUtils.gcd(fraction.numerator, denominator); return getReducedFraction
return getReducedFraction (MathUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
(MathUtils.mulAndCheck(numerator/d1, fraction.numerator/d2), MathUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
MathUtils.mulAndCheck(denominator/d2, fraction.denominator/d1)); }
}
/**
/** * <p>Divide the value of this fraction by another.</p>
* <p>Divide the value of this fraction by another.</p> *
* * @param fraction the fraction to divide by, must not be <code>null</code>
* @param fraction the fraction to divide by, must not be <code>null</code> * @return a <code>Fraction</code> instance with the resulting values
* @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code>
* @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the fraction to divide by is zero
* @throws ArithmeticException if the fraction to divide by is zero * @throws ArithmeticException if the resulting numerator or denominator exceeds
* @throws ArithmeticException if the resulting numerator or denominator exceeds * <code>Integer.MAX_VALUE</code>
* <code>Integer.MAX_VALUE</code> */
*/ public Fraction divide(Fraction fraction) {
public Fraction divide(Fraction fraction) { if (fraction == null) {
if (fraction == null) { throw new IllegalArgumentException("The fraction must not be null");
throw new IllegalArgumentException("The fraction must not be null"); }
} if (fraction.numerator == 0) {
if (fraction.numerator == 0) { throw new ArithmeticException("The fraction to divide by must not be zero");
throw new ArithmeticException("The fraction to divide by must not be zero"); }
} return multiply(fraction.reciprocal());
return multiply(fraction.reciprocal()); }
}
/**
/** * <p>Creates a <code>Fraction</code> instance with the 2 parts
* <p>Creates a <code>Fraction</code> instance with the 2 parts * of a fraction Y/Z.</p>
* of a fraction Y/Z.</p> *
* * <p>Any negative signs are resolved to be on the numerator.</p>
* <p>Any negative signs are resolved to be on the numerator.</p> *
* * @param numerator the numerator, for example the three in 'three sevenths'
* @param numerator the numerator, for example the three in 'three sevenths' * @param denominator the denominator, for example the seven in 'three sevenths'
* @param denominator the denominator, for example the seven in 'three sevenths' * @return a new fraction instance, with the numerator and denominator reduced
* @return a new fraction instance, with the numerator and denominator reduced * @throws ArithmeticException if the denominator is <code>zero</code>
* @throws ArithmeticException if the denominator is <code>zero</code> */
*/ public static Fraction getReducedFraction(int numerator, int denominator) {
public static Fraction getReducedFraction(int numerator, int denominator) { if (denominator == 0) {
if (denominator == 0) { throw new ArithmeticException("The denominator must not be zero");
throw new ArithmeticException("The denominator must not be zero"); }
} if (numerator==0) {
if (numerator==0) { return ZERO; // normalize zero.
return ZERO; // normalize zero. }
} // allow 2^k/-2^31 as a valid fraction (where k>0)
// allow 2^k/-2^31 as a valid fraction (where k>0) if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
if (denominator==Integer.MIN_VALUE && (numerator&1)==0) { numerator/=2; denominator/=2;
numerator/=2; denominator/=2; }
} if (denominator < 0) {
if (denominator < 0) { if (numerator==Integer.MIN_VALUE ||
if (numerator==Integer.MIN_VALUE || denominator==Integer.MIN_VALUE) {
denominator==Integer.MIN_VALUE) { throw new ArithmeticException("overflow: can't negate");
throw new ArithmeticException("overflow: can't negate"); }
} numerator = -numerator;
numerator = -numerator; denominator = -denominator;
denominator = -denominator; }
} // simplify fraction.
// simplify fraction. int gcd = MathUtils.gcd(numerator, denominator);
int gcd = MathUtils.gcd(numerator, denominator); numerator /= gcd;
numerator /= gcd; denominator /= gcd;
denominator /= gcd; return new Fraction(numerator, denominator);
return new Fraction(numerator, denominator); }
}
/**
/** * Reduce this fraction to lowest terms. This is accomplished by dividing
* Reduce this fraction to lowest terms. This is accomplished by dividing * both numerator and denominator by their greatest common divisor.
* both numerator and denominator by their greatest common divisor. */
*/ private void reduce() {
private void reduce() { // reduce numerator and denominator by greatest common denominator.
// reduce numerator and denominator by greatest common denominator. int d = MathUtils.gcd(numerator, denominator);
int d = MathUtils.gcd(numerator, denominator); if (d > 1) {
if (d > 1) { numerator /= d;
numerator /= d; denominator /= d;
denominator /= d; }
}
// move sign to numerator.
// move sign to numerator. if (denominator < 0) {
if (denominator < 0) { numerator *= -1;
numerator *= -1; denominator *= -1;
denominator *= -1; }
} }
} }
}

View File

@ -0,0 +1,22 @@
package org.apache.commons.math.fraction;
import org.apache.commons.math.MaxIterationsExceededException;
public class FractionConversionException extends MaxIterationsExceededException {
/** Serializable version identifier. */
private static final long serialVersionUID = 4588659344016668813L;
/**
* Constructs an exception with specified formatted detail message.
* Message formatting is delegated to {@link java.text.MessageFormat}.
* @param value double value to convert
* @param maxIterations maximal number of iterations allowed
*/
public FractionConversionException(double value, int maxIterations) {
super(maxIterations,
"Unable to convert {0} to fraction after {1} iterations",
new Object[] { new Double(value), new Integer(maxIterations) });
}
}