Académique Documents
Professionnel Documents
Culture Documents
*;
/**
* Arbitrary-precision fractions, utilizing BigIntegers for numerator and
* denominator. Fraction is always kept in lowest terms. Fraction is
* immutable, and guaranteed not to have a null numerator or denominator.
* Denominator will always be positive (so sign is carried by numerator,
* and a zero-denominator is impossible).
*/
public final class BigFraction extends Number implements Comparable<BigFraction>
{
private static final long serialVersionUID = 1L; //because Number is Serializable
private final BigInteger numerator;
private final BigInteger denominator;
/**
* Constructs a BigFraction with given numerator and denominator. Fraction
* will be reduced to lowest terms. If fraction is negative, negative sign will
* be carried on numerator, regardless of how the values were passed in.
*/
public BigFraction(BigInteger numerator, BigInteger denominator)
{
if(numerator == null)
throw new IllegalArgumentException("Numerator is null");
if(denominator == null)
throw new IllegalArgumentException("Denominator is null");
if(denominator.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero.");
/**
* Constructs a BigFraction from a whole number.
*/
public BigFraction(BigInteger numerator)
{
this(numerator, BigInteger.ONE, true);
}
/**
* Constructs a BigFraction from a floating-point number.
*
* Warning: round-off error in IEEE floating point numbers can result
* in answers that are unexpected. For example,
* System.out.println(new BigFraction(1.1))
* will print:
* 2476979795053773/2251799813685248
*
* This is because 1.1 cannot be expressed exactly in binary form. The
* given fraction is exactly equal to the internal representation of
* the double-precision floating-point number. (Which, for 1.1, is:
* (-1)^0 * 2^0 * (1 + 0x199999999999aL / 0x10000000000000L).)
*
* NOTE: In many cases, BigFraction(Double.toString(d)) may give a result
* closer to what the user expects.
*/
public BigFraction(double d)
{
if(Double.isInfinite(d))
throw new IllegalArgumentException("double val is infinite");
if(Double.isNaN(d))
throw new IllegalArgumentException("double val is NaN");
//special case - math below won't work right for 0.0 or -0.0
if(d == 0)
{
numerator = BigInteger.ZERO;
denominator = BigInteger.ONE;
return;
}
/**
* Constructs a BigFraction from two floating-point numbers.
*
* Warning: round-off error in IEEE floating point numbers can result
* in answers that are unexpected. See BigFraction(double) for more
* information.
*
* NOTE: In many cases, BigFraction(Double.toString(numerator) + "/" +
Double.toString(denominator))
* may give a result closer to what the user expects.
*/
public BigFraction(double numerator, double denominator)
{
if(denominator == 0)
throw new ArithmeticException("Divide by zero.");
/**
* Constructs a new BigFraction from the given BigDecimal object.
*/
public BigFraction(BigDecimal d)
{
this(d.scale() < 0 ? d.unscaledValue().multiply(BigInteger.TEN.pow(-d.scale()))
: d.unscaledValue(),
d.scale() < 0 ? BigInteger.ONE
: BigInteger.TEN.pow(d.scale()));
}
/**
* Constructs a BigFraction from a String. Expected format is
numerator/denominator,
* but /denominator part is optional. Either numerator or denominator may be a
floating-
* point decimal number, which in the same format as a parameter to the
* <code>BigDecimal(String)</code> constructor.
*
* @throws NumberFormatException if the string cannot be properly parsed.
*/
public BigFraction(String s)
{
int slashPos = s.indexOf('/');
if(slashPos < 0)
{
BigFraction res = new BigFraction(new BigDecimal(s));
this.numerator = res.numerator;
this.denominator = res.denominator;
}
else
{
BigDecimal num = new BigDecimal(s.substring(0, slashPos));
BigDecimal den = new BigDecimal(s.substring(slashPos+1, s.length()));
BigFraction res = new BigFraction(num, den);
this.numerator = res.numerator;
this.denominator = res.denominator;
}
}
/**
* Returns this + f.
*/
public BigFraction add(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns this + b.
*/
public BigFraction add(BigInteger b)
{
if(b == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns this + n.
*/
public BigFraction add(long n)
{
return add(BigInteger.valueOf(n));
}
/**
* Returns this - f.
*/
public BigFraction subtract(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
return new
BigFraction(numerator.multiply(f.denominator).subtract(denominator.multiply(f.numer
ator)),
denominator.multiply(f.denominator));
}
/**
* Returns this - b.
*/
public BigFraction subtract(BigInteger b)
{
if(b == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns this - n.
*/
public BigFraction subtract(long n)
{
return subtract(BigInteger.valueOf(n));
}
/**
* Returns this * f.
*/
public BigFraction multiply(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns this * b.
*/
public BigFraction multiply(BigInteger b)
{
if(b == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns this * n.
*/
public BigFraction multiply(long n)
{
return multiply(BigInteger.valueOf(n));
}
/**
* Returns this / f.
*/
public BigFraction divide(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
if(f.numerator.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero");
/**
* Returns this / b.
*/
public BigFraction divide(BigInteger b)
{
if(b == null)
throw new IllegalArgumentException("Null argument");
if(b.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero");
/**
* Returns this / n.
*/
public BigFraction divide(long n)
{
return divide(BigInteger.valueOf(n));
}
/**
* Returns this^exponent.
*/
public BigFraction pow(int exponent)
{
if(exponent == 0)
return BigFraction.ONE;
else if (exponent == 1)
return this;
else if (exponent < 0)
return new BigFraction(denominator.pow(-exponent), numerator.pow(-exponent),
true);
else
return new BigFraction(numerator.pow(exponent), denominator.pow(exponent),
true);
}
/**
* Returns 1/this.
*/
public BigFraction reciprocal()
{
if(this.numerator.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by zero");
/**
* Returns the complement of this fraction, which is equal to 1 - this.
* Useful for probabilities/statistics.
*/
public BigFraction complement()
{
return new BigFraction(denominator.subtract(numerator), denominator, true);
}
/**
* Returns -this.
*/
public BigFraction negate()
{
return new BigFraction(numerator.negate(), denominator, true);
}
/**
* Returns -1, 0, or 1, representing the sign of this fraction.
*/
public int signum()
{
return numerator.signum();
}
/**
* Returns the absolute value of this.
*/
public BigFraction abs()
{
return (signum() < 0 ? negate() : this);
}
/**
* Returns a string representation of this, in the form
* numerator/denominator.
*/
public String toString()
{
return numerator.toString() + "/" + denominator.toString();
}
/**
* Returns if this object is equal to another object.
*/
public boolean equals(Object o)
{
if(!(o instanceof BigFraction))
return false;
BigFraction f = (BigFraction)o;
return numerator.equals(f.numerator) && denominator.equals(f.denominator);
}
/**
* Returns a hash code for this object.
*/
public int hashCode()
{
//using the method generated by Eclipse, but streamlined a bit..
return (31 + numerator.hashCode())*31 + denominator.hashCode();
}
/**
* Returns a negative, zero, or positive number, indicating if this object
* is less than, equal to, or greater than f, respectively.
*/
public int compareTo(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
//not an easy case, so first make the denominators equal then compare the
numerators
return
numerator.multiply(f.denominator).compareTo(denominator.multiply(f.numerator));
}
/**
* Returns the smaller of this and f.
*/
public BigFraction min(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns the maximum of this and f.
*/
public BigFraction max(BigFraction f)
{
if(f == null)
throw new IllegalArgumentException("Null argument");
/**
* Returns a BigDecimal representation of this fraction. If possible, the
* returned value will be exactly equal to the fraction. If not, the BigDecimal
* will have a scale large enough to hold the same number of significant figures
* as both numerator and denominator, or the equivalent of a double-precision
* number, whichever is more.
*/
public BigDecimal toBigDecimal()
{
//Implementation note: A fraction can be represented exactly in base-10 iff
its
//denominator is of the form 2^a * 5^b, where a and b are nonnegative integers.
//(In other words, if there are no prime factors of the denominator except for
//2 and 5, or if the denominator is 1). So to determine if this denominator is
//of this form, continually divide by 2 to get the number of 2's, and then
//continually divide by 5 to get the number of 5's. Afterward, if the
denominator
//is 1 then there are no other prime factors.
//Note: number of 2's is given by the number of trailing 0 bits in the number
int twos = denominator.getLowestSetBit();
BigInteger tmpDen = denominator.shiftRight(twos); // x / 2^n === x >> n
if(BigInteger.ONE.equals(tmpDen))
{
//This fraction will terminate in base 10, so it can be represented exactly
as
//a BigDecimal. We would now like to make the fraction of the form
//unscaled / 10^scale. We know that 2^x * 5^x = 10^x, and our denominator is
//in the form 2^twos * 5^fives. So use max(twos, fives) as the scale, and
//multiply the numerator and deminator by the appropriate number of 2's or
5's
//such that the denominator is of the form 2^scale * 5^scale. (Of course, we
//only have to actually multiply the numerator, since all we need for the
//BigDecimal constructor is the scale.
BigInteger unscaled = numerator;
int scale = Math.max(twos, fives);
//else: this number will repeat infinitely in base-10. So try to figure out
//a good number of significant digits. Start with the number of digits
required
//to represent the numerator and denominator in base-10, which is given by
//bitLength / log[2](10). (bitLenth is the number of digits in base-2).
final double LG10 = 3.321928094887362; //Precomputed ln(10)/ln(2), a.k.a.
log[2](10)
int precision = Math.max(numerator.bitLength(), denominator.bitLength());
precision = (int)Math.ceil(precision / LG10);
//If the precision is less than 18 digits, use 18 digits so that the number
//will be at least as accurate as a cast to a double. For example, with
//the fraction 1/3, precision will be 1, giving a result of 0.3. This is
//quite a bit different from what a user would expect.
if(precision < 18)
precision = 18;
return toBigDecimal(precision);
}
/**
* Returns a BigDecimal representation of this fraction, with a given precision.
* @param precision the number of significant figures to be used in the result.
*/
public BigDecimal toBigDecimal(int precision)
{
return new BigDecimal(numerator).divide(new BigDecimal(denominator), new
MathContext(precision, RoundingMode.HALF_EVEN));
}
//--------------------------------------------------------------------------
// PRIVATE FUNCTIONS
//--------------------------------------------------------------------------
/**
* Private constructor, used when you can be certain that the fraction is already
in
* lowest terms. No check is done to reduce numerator/denominator. A check is
still
* done to maintain a positive denominator.
*
* @param throwaway unused variable, only here to signal to the compiler that
this
* constructor should be used.
*/
private BigFraction(BigInteger numerator, BigInteger denominator, boolean
throwaway)
{
if(denominator.signum() < 0)
{
this.numerator = numerator.negate();
this.denominator = denominator.negate();
}
else
{
this.numerator = numerator;
this.denominator = denominator;
}
}