/** * Multiplies the value of this fraction by another, returning the result in reduced form. * * @param fraction the fraction to multiply by, must not be <code>null</code> * @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator exceeds <code> * Integer.MAX_VALUE</code> */ public Fraction multiply(Fraction fraction) { if (fraction == null) { throw MathRuntimeException.createIllegalArgumentException("null fraction"); } if (numerator == 0 || fraction.numerator == 0) { return ZERO; } // knuth 4.5.1 // make sure we don't overflow unless the result *must* overflow. int d1 = MathUtils.gcd(numerator, fraction.denominator); int d2 = MathUtils.gcd(fraction.numerator, denominator); return getReducedFraction( MathUtils.mulAndCheck(numerator / d1, fraction.numerator / d2), MathUtils.mulAndCheck(denominator / d2, fraction.denominator / d1)); }
/** * Creates a <code>Fraction</code> instance with the 2 parts of a fraction Y/Z. * * <p>Any negative signs are resolved to be on the numerator. * * @param numerator the numerator, for example the three 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 * @throws ArithmeticException if the denominator is <code>zero</code> */ public static Fraction getReducedFraction(int numerator, int denominator) { if (denominator == 0) { throw MathRuntimeException.createArithmeticException( "zero denominator in fraction {0}/{1}", numerator, denominator); } if (numerator == 0) { return ZERO; // normalize zero. } // allow 2^k/-2^31 as a valid fraction (where k>0) if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) { numerator /= 2; denominator /= 2; } if (denominator < 0) { if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) { throw MathRuntimeException.createArithmeticException( "overflow in fraction {0}/{1}, cannot negate", numerator, denominator); } numerator = -numerator; denominator = -denominator; } // simplify fraction. int gcd = MathUtils.gcd(numerator, denominator); numerator /= gcd; denominator /= gcd; return new Fraction(numerator, denominator); }
/** * Create a fraction given the numerator and denominator. The fraction is reduced to lowest terms. * * @param num the numerator. * @param den the denominator. * @throws ArithmeticException if the denominator is <code>zero</code> */ public Fraction(int num, int den) { if (den == 0) { throw MathRuntimeException.createArithmeticException( "zero denominator in fraction {0}/{1}", num, den); } if (den < 0) { if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) { throw MathRuntimeException.createArithmeticException( "overflow in fraction {0}/{1}, cannot negate", num, den); } num = -num; den = -den; } // reduce numerator and denominator by greatest common denominator. final int d = MathUtils.gcd(num, den); if (d > 1) { num /= d; den /= d; } // move sign to numerator. if (den < 0) { num = -num; den = -den; } this.numerator = num; this.denominator = den; }
/** * Creates a <code>Fraction</code> instance with the 2 parts of a fraction Y/Z. * * <p>Any negative signs are resolved to be on the numerator. * * @param numerator the numerator, for example the three 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 * @throws ArithmeticException if the denominator is <code>zero</code> */ public static Fraction getReducedFraction(int numerator, int denominator) { if (denominator == 0) { throw MathRuntimeException.createArithmeticException( ZERO_DENOMINATOR_MESSAGE, numerator, denominator); } if (numerator == 0) { return ZERO; // normalize zero. } // allow 2^k/-2^31 as a valid fraction (where k>0) if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) { numerator /= 2; denominator /= 2; } if (denominator < 0) { if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) { throw MathRuntimeException.createArithmeticException( OVERFLOW_MESSAGE, numerator, denominator); } numerator = -numerator; denominator = -denominator; } // simplify fraction. int gcd = MathUtils.gcd(numerator, denominator); numerator /= gcd; denominator /= gcd; return new Fraction(numerator, denominator); }
/** * Create a fraction given the numerator and denominator. The fraction is reduced to lowest terms. * * @param num the numerator. * @param den the denominator. * @throws ArithmeticException if the denominator is <code>zero</code> */ public Fraction(int num, int den) { if (den == 0) { throw MathRuntimeException.createArithmeticException(ZERO_DENOMINATOR_MESSAGE, num, den); } if (den < 0) { if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) { throw MathRuntimeException.createArithmeticException(OVERFLOW_MESSAGE, num, den); } num = -num; den = -den; } // reduce numerator and denominator by greatest common denominator. final int d = MathUtils.gcd(num, den); if (d > 1) { num /= d; den /= d; } // move sign to numerator. if (den < 0) { num = -num; den = -den; } this.numerator = num; this.denominator = den; }
/** * 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 isAdd true to add, false to subtract * @return a <code>Fraction</code> instance with the resulting values * @throws IllegalArgumentException if the fraction is <code>null</code> * @throws ArithmeticException if the resulting numerator or denominator cannot be represented in * an <code>int</code>. */ private Fraction addSub(Fraction fraction, boolean isAdd) { if (fraction == null) { throw MathRuntimeException.createIllegalArgumentException("null fraction"); } // zero is identity for addition. if (numerator == 0) { return isAdd ? fraction : fraction.negate(); } if (fraction.numerator == 0) { return this; } // if denominators are randomly distributed, d1 will be 1 about 61% // of the time. int d1 = MathUtils.gcd(denominator, fraction.denominator); if (d1 == 1) { // result is ( (u*v' +/- u'v) / u'v') int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator); int upv = MathUtils.mulAndCheck(fraction.numerator, denominator); return new Fraction( isAdd ? MathUtils.addAndCheck(uvp, upv) : MathUtils.subAndCheck(uvp, upv), MathUtils.mulAndCheck(denominator, fraction.denominator)); } // the quantity 't' requires 65 bits of precision; see knuth 4.5.1 // exercise 7. we're going to use a BigInteger. // t = u(v'/d1) +/- v(u'/d1) BigInteger uvp = BigInteger.valueOf(numerator).multiply(BigInteger.valueOf(fraction.denominator / d1)); BigInteger upv = BigInteger.valueOf(fraction.numerator).multiply(BigInteger.valueOf(denominator / d1)); BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); // but d2 doesn't need extra precision because // d2 = gcd(t,d1) = gcd(t mod d1, d1) int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); int d2 = (tmodd1 == 0) ? d1 : MathUtils.gcd(tmodd1, d1); // result is (t/d2) / (u'/d1)(v'/d2) BigInteger w = t.divide(BigInteger.valueOf(d2)); if (w.bitLength() > 31) { throw MathRuntimeException.createArithmeticException( "overflow, numerator too large after multiply: {0}", w); } return new Fraction( w.intValue(), MathUtils.mulAndCheck(denominator / d1, fraction.denominator / d2)); }