Code Review

Anywho, I've wanted (for a while, yet) a BigFraction class. So I made one. I'd like to get comments on it, though, so:

/* Author: Adeodatus

*

*

*/

/**package java.math;**/

import java.math.*;

publicclass BigFractionextends Numberimplements Comparable<BigFraction>

{

publicstaticfinal BigFraction ZERO =new BigFraction(BigInteger.ZERO, BigInteger.ONE),

ONE =new BigFraction(BigInteger.ONE, BigInteger.ONE),

TEN =new BigFraction(BigInteger.TEN, BigInteger.ONE);

private/*final*/ BigInteger n,//numerator

d;//denominator

privatefinal BigDecimal m;

privatestaticfinal BigInteger TWO = BigInteger.valueOf(2);

privatestaticfinal java.security.SecureRandom rng;

static

{

rng =new java.security.SecureRandom();

rng.setSeed(BigInteger.valueOf(System.currentTimeMillis() * 1000000/*+ System.nanoTime()*/).pow(65).toByteArray());

}

public BigFraction(char[] in)

{

this(in, 0, in.length);

}

public BigFraction(char[] in,int offset,int len)

{

this(new String(in).substring(offset, len));

}

public BigFraction(String val)

{

int index = val.indexOf('/');

if(index < 0)

thrownew ArithmeticException("Constructor must be passed a fraction.");

n =new BigInteger(val.substring(0, index));

d =new BigInteger(val.substring(index + 1, val.length()));

m =new BigDecimal(n).divide(new BigDecimal(d), MathContext.DECIMAL64);

}

public BigFraction(String numerator, String denominator)

{

this(new BigInteger(numerator),new BigInteger(denominator));

}

public BigFraction(BigDecimal bd)

{

int scale = bd.scale();

if(scale >= 0)

{

n = bd.unscaledValue().multiply(BigInteger.TEN.pow(scale));

d = BigInteger.ONE;

}

else

{

n = bd.unscaledValue();

d = BigInteger.TEN.pow(-scale);

}

m = bd;

}

public BigFraction(BigFraction val)

{

n = val.n;

d = val.d;

m = val.m;

}

public BigFraction(BigInteger numerator)

{

this(numerator, BigInteger.ONE);

}

public BigFraction(byte[] numerator,byte[] denominator)

{

this(new BigInteger(numerator),new BigInteger(denominator));

}

public BigFraction(BigInteger numerator, BigInteger denominator)

{

if(BigInteger.ZERO.equals(denominator))

thrownew ArithmeticException("Division by Zero");

n = numerator;

d = denominator;

m =new BigDecimal(n).divide(new BigDecimal(d), MathContext.DECIMAL128);

}

public BigFraction abs()

{

if(n.signum() >= 0 && d.signum() == 1)

returnthis;

returnnew BigFraction(n.abs(), d.abs());

}

public BigFraction add(BigFraction augend)

{

returnnew BigFraction(n.multiply(augend.d).add(augend.n.multiply(d)), d.multiply(augend.d)).simp();

}

publicbyte byteValueExact()

{

return m.byteValueExact();

}

publicbyte byteValue()

{

return m.byteValue();

}

publicint compareTo(BigFraction val)

{

ints = signum(),

vals = val.signum();

return s == vals ? this.subtract(val).signum() : (s > vals ? 1 : -1);

}

public BigInteger denominator()

{

return d;

}

public BigFraction divide(BigFraction divisor)

{

returnnew BigFraction(n.multiply(divisor.d), d.multiply(divisor.n));

}

public BigFraction[] divideAndRemainder(BigFraction divisor)

{

BigFraction[] ans =new BigFraction[2];

ans[0] = divideToIntegralValue(divisor);

ans[1] = subtract(divisor.multiply(ans[0]));

return ans;

}

public BigFraction divideToIntegralValue(BigFraction divisor)

{

returnnew BigFraction(n.divide(d), BigInteger.ONE);

}

publicdouble doubleValue()

{

return m.doubleValue();

}

publicboolean equals(Object o)

{

return oinstanceof BigFraction ? equals((BigFraction) o) :false;

}

publicboolean equals(BigFraction val)

{

if(this == val)

returntrue;

BigFraction a = this.simplify(),

b = val.simplify();

return a.d.equals(b.d) && a.n.equals(b.n);

}

publicfloat floatValue()

{

return m.floatValue();

}

publicint hashCode()

{

return n.hashCode()+d.hashCode();

}

publicint intValueExact()

{

return m.intValueExact();

}

publicint intValue()

{

return m.intValue();

}

public BigFraction inverse()

{

returnnew BigFraction(d, n);

}

publicboolean isSimplified()

{

return (d.signum() == 0) && n.gcd(d).equals(BigInteger.ONE);

}

publiclong longValueExact()

{

return m.longValueExact();

}

publiclong longValue()

{

return m.longValue();

}

public BigFraction max(BigFraction val)

{

return compareTo(val) >= 0 ?this : val;

}

public BigFraction min(BigFraction val)

{

return compareTo(val) <= 0 ?this : val;

}

public BigFraction mod(BigFraction m)

{

return remainder(m).abs();

}

public BigFraction multiply(BigFraction multiplicand)

{

returnnew BigFraction(n.multiply(multiplicand.n), d.multiply(multiplicand.d));

}

public BigFraction negate()

{

returnnew BigFraction(n.negate(), d);

}

public BigInteger numerator()

{

return n;

}

public BigFraction plus()

{

returnthis;

}

public BigFraction pow(int power)

{

BigInteger n = this.n,

d = this.d;

if(power < 0)

{

power = -power;

n = this.d;

d = this.n;

}

returnnew BigFraction(n.pow(power), d.pow(power));

}

publicstatic BigFraction random()

{

return random(128, rng);

}

publicstatic BigFraction random(int bits)

{

return random(bits, rng);

}

publicstatic BigFraction random(int bits, java.util.Random rng)

{

returnnew BigFraction(new BigInteger(bits, rng), TWO.pow(bits));

}

public BigFraction remainder(BigFraction divisor)

{

return subtract(divisor.multiply(divideToIntegralValue(divisor)));

}

publicshort shortValueExact()

{

return m.shortValueExact();

}

publicshort shortValue()

{

return m.shortValue();

}

publicint signum()

{

return m.signum();

}

protected BigFraction simp()

{//helper method: Used to return simplified fractions from other methods

BigInteger gcd = n.gcd(d);

if(d.signum() == 1)

{

if(!gcd.equals(BigInteger.ONE))

{

n = n.divide(gcd);

d = d.divide(gcd);

}

}

else

{

if(gcd.equals(BigInteger.ONE))

{

n = n.negate();

d = d.negate();

}

else

{

n = n.divide(gcd).negate();

d = d.divide(gcd).negate();

}

}

returnthis;

}

public BigFraction simplify()

{

BigInteger gcd = n.gcd(d);

if(d.signum() == 1)

{

if(gcd.equals(BigInteger.ONE))

returnthis;

returnnew BigFraction(n.divide(gcd), d.divide(gcd));

}

else

returnnew BigFraction(n.divide(gcd).negate(), d.divide(gcd).negate());

}

public BigFraction subtract(BigFraction subtrahend)

{

returnnew BigFraction(n.multiply(subtrahend.d).subtract(subtrahend.n.multiply(d)), d.multiply(subtrahend.d)).simp();

}

public BigDecimal toBigDecimal()

{

return toBigDecimal(MathContext.UNLIMITED);

}

public BigDecimal toBigDecimal(int precision)

{

return toBigDecimal(new MathContext(precision));

}

public BigDecimal toBigDecimal(MathContext m)

{

returnnew BigDecimal(n).divide(new BigDecimal(d), m);

}

public BigInteger toBigInteger()

{

return n.divide(d);

}

BigInteger toBigIntegerExact()

{

BigInteger[] div = n.divideAndRemainder(d);

if(div[1].equals(BigInteger.ZERO))

thrownew ArithmeticException("Information lost in conversion to BigInteger.");

return div[0];

}

public String toDecimalString()

{

return toDecimalString(MathContext.UNLIMITED);

}

public String toDecimalString(int precision)

{

return toDecimalString(new MathContext(precision));

}

public String toDecimalString(MathContext m)

{

return toBigDecimal(m).toString();

}

public String toString()

{

return toString(10);

}

public String toString(int base)

{

return n.toString(base) +"/" + d.toString(base);

}

publicstatic BigFraction valueOf(double val)

{

returnnew BigFraction(BigDecimal.valueOf(val));

}

publicstatic BigFraction valueOf(long val)

{

return valueOf(val, 1);

}

publicstatic BigFraction valueOf(long numerator,long denominator)

{

returnnew BigFraction(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));

}

publicstatic BigFraction valueOf(String val)

{

int index = val.indexOf('/');

if(index >= 0)

returnnew BigFraction(val);

else

returnnew BigFraction(new BigDecimal(val));

}

}

[22378 byte] By [Adeodatus] at [2007-9-30 20:56:22]
# 1

I noticed this page is te first hit on google for "BigFraction." Not wanting to foul anyone up with the bugs in my first version, I'm posting my fixed-up version ;~)

/* Author: Adeodatus

*

*

*/

/*package java.math;*/

import java.math.*;

public class BigFraction extends Number implements Comparable<BigFraction>, java.io.Serializable

{

public static final BigFraction ZERO = new BigFraction(BigInteger.ZERO, BigInteger.ONE),

ONE = new BigFraction(BigInteger.ONE, BigInteger.ONE),

TEN = new BigFraction(BigInteger.TEN, BigInteger.ONE);

private final BigInteger n, //numerator

d; //denominator

private static final BigInteger TWO = BigInteger.valueOf(2);

private static final java.security.SecureRandom rng;

static

{

rng = new java.security.SecureRandom();

rng.setSeed(BigInteger.valueOf(System.currentTimeMillis() * 1000000 + System.nanoTime()).pow(65).subtract(BigInteger.valueOf(13)).toByteArray());

}

public BigFraction()

{

this(BigInteger.ZERO, BigInteger.ONE);

}

public BigFraction(String val)

{

this(val, val.indexOf('/'));

}

public BigFraction(String val, int radix)

{

int index = val.indexOf('/');

if(index < 0)

throw new NumberFormatException("Constructor must be passed a properly formatted String.");

try

{

BigInteger n = new BigInteger(val.substring(0, index), radix),

d = new BigInteger(val.substring(index + 1, val.length()), radix),

gcd = n.gcd(d);

if(d.signum() == 0)

throw new ArithmeticException("Division by Zero");

else if(d.signum() == -1)

{

n = n.negate();

d = d.negate();

}

if(!gcd.equals(BigInteger.ONE))

{

n = n.divide(gcd);

d = d.divide(gcd);

}

this.n = n;

this.d = d;

}

catch(ArithmeticException e)

{

throw new ArithmeticException("Constructor must be passed a properly formatted String.");

}

}

public BigFraction(String numerator, String denominator)

{

this(new BigInteger(numerator), new BigInteger(denominator));

}

public BigFraction(BigDecimal bd)

{

int scale = bd.scale();

BigInteger n, d;

if(bd.signum() == 0)

{

n = BigInteger.ONE;

d = BigInteger.ZERO;

}

else

{

if(scale >= 0)

{

n = bd.unscaledValue().multiply(BigInteger.TEN.pow(scale));

d = BigInteger.ONE;

}

else

{

n = bd.unscaledValue();

d = BigInteger.TEN.pow(-scale);

}

BigInteger gcd = n.gcd(d);

if(!gcd.equals(BigInteger.ONE))

{

n = n.divide(gcd);

d = d.divide(gcd);

}

}

this.n = n;

this.d = d;

}

public BigFraction(BigInteger numerator)

{

this(numerator, BigInteger.ONE);

}

public BigFraction(byte[] numerator, byte[] denominator)

{

this(new BigInteger(numerator), new BigInteger(denominator));

}

public BigFraction(BigInteger numerator, BigInteger denominator)

{

if(denominator.signum() == 0)

throw new ArithmeticException("Division by Zero");

BigInteger n = numerator,

d = denominator,

gcd = n.gcd(d);

if(n.signum() == 0)

{

n = BigInteger.ZERO;

d = BigInteger.ONE;

}

else

{

if(d.signum() == -1)

{

n = n.negate();

d = d.negate();

}

if(!gcd.equals(BigInteger.ONE))

{

n = n.divide(gcd);

d = d.divide(gcd);

}

}

this.n = n;

this.d = d;

}

public BigFraction abs()

{

return n.signum() >= 0 ? this : new BigFraction(n.negate(), d);

}

public BigFraction add(BigFraction augend)

{

return new BigFraction(n.multiply(augend.d).add(augend.n.multiply(d)), d.multiply(augend.d));

}

public byte byteValue()

{

return toBigInteger().byteValue();

}

public byte byteValueExact()

{

BigInteger b = toBigIntegerExact();

if(isInRange(b, BigInteger.valueOf(Byte.MAX_VALUE), BigInteger.valueOf(Byte.MIN_VALUE)))

return b.byteValue();

throw new ArithmeticException("BigFraction out of range for byte");

}

public int compareTo(BigFraction val)

{

if(signum() == val.signum())

{

if(signum() == 0)

return 0;

return n.multiply(val.d).compareTo(val.n.multiply(d));

}

return signum() > val.signum() ? 1 : -1;

}

public BigInteger denominator()

{

return d;

}

public BigFraction divide(BigFraction divisor)

{

return new BigFraction(n.multiply(divisor.d), d.multiply(divisor.n));

}

public BigFraction[] divideAndRemainder(BigFraction divisor)

{

BigFraction[] ans = new BigFraction[2];

ans[0] = divideToIntegralValue(divisor);

ans[1] = subtract(divisor.multiply(ans[0]));

return ans;

}

public BigFraction divideToIntegralValue(BigFraction divisor)

{

return new BigFraction(divide(divisor).toBigInteger(), BigInteger.ONE);

}

public double doubleValue()

{

return toBigDecimal(MathContext.DECIMAL64).doubleValue();

}

public boolean equals(Object o)

{

return o instanceof BigFraction ? equals((BigFraction) o) : false;

}

public boolean equals(BigFraction val)

{

if(this == val)

return true;

return val != null && n.equals(val.n) && d.equals(val.d);

}

public float floatValue()

{

return toBigDecimal(MathContext.DECIMAL32).floatValue();

}

public int hashCode()

{

return n.hashCode() ^ d.hashCode();

}

public int intValue()

{

return toBigInteger().intValue();

}

public int intValueExact()

{

BigInteger b = toBigIntegerExact();

if(isInRange(b, BigInteger.valueOf(Integer.MAX_VALUE), BigInteger.valueOf(Integer.MIN_VALUE)))

return b.intValue();

throw new ArithmeticException("BigFraction out of range for int");

}

public BigFraction inverse()

{

return new BigFraction(d, n);

}

private boolean isInRange(BigInteger test, BigInteger min, BigInteger max)

{

return test.compareTo(min) >= 0 && test.compareTo(max) <= 0;

}

public long longValue()

{

return toBigInteger().longValue();

}

public long longValueExact()

{

BigInteger b = toBigIntegerExact();

if(isInRange(b, BigInteger.valueOf(Long.MAX_VALUE), BigInteger.valueOf(Long.MIN_VALUE)))

return b.longValue();

throw new ArithmeticException("BigFraction out of range for long");

}

public BigFraction max(BigFraction val)

{

return compareTo(val) >= 0 ? this : val;

}

public BigFraction min(BigFraction val)

{

return compareTo(val) <= 0 ? this : val;

}

public BigFraction mod(BigFraction val)

{

return remainder(val).abs();

}

public BigFraction multiply(BigFraction multiplicand)

{

return new BigFraction(n.multiply(multiplicand.n), d.multiply(multiplicand.d));

}

public BigFraction negate()

{

return new BigFraction(n.negate(), d);

}

public BigInteger numerator()

{

return n;

}

public BigFraction plus()

{

return this;

}

public BigFraction pow(int power)

{

BigInteger n = this.n,

d = this.d;

if(power < 0)

{

power = -power;

n = this.d;

d = this.n;

}

return new BigFraction(n.pow(power), d.pow(power));

}

public static BigFraction random()

{

return random(128, rng);

}

public static BigFraction random(int bits)

{

return random(bits, rng);

}

public static BigFraction random(int bits, java.util.Random rng)

{

try

{

return new BigFraction(new BigInteger(bits, rng), TWO.pow(bits));

}

catch(IllegalArgumentException e)

{

throw new IllegalArgumentException("Bit length may not be negative.", e);

}

}

public BigFraction remainder(BigFraction divisor)

{

return subtract(divisor.multiply(divideToIntegralValue(divisor)));

}

public short shortValue()

{

return toBigInteger().shortValue();

}

public short shortValueExact()

{

BigInteger b = toBigIntegerExact();

if(isInRange(b, BigInteger.valueOf(Short.MAX_VALUE), BigInteger.valueOf(Short.MIN_VALUE)))

return b.shortValue();

throw new ArithmeticException("BigFraction out of range for short");

}

public int signum()

{

return n.signum();

}

public BigFraction subtract(BigFraction subtrahend)

{

return new BigFraction(n.multiply(subtrahend.d).subtract(subtrahend.n.multiply(d)), d.multiply(subtrahend.d));

}

public BigDecimal toBigDecimal()

{

return toBigDecimal(MathContext.UNLIMITED);

}

public BigDecimal toBigDecimal(MathContext mc)

{

return new BigDecimal(n).divide(new BigDecimal(d), mc);

}

public BigInteger toBigInteger()

{

return n.divide(d);

}

BigInteger toBigIntegerExact()

{

BigInteger[] div = n.divideAndRemainder(d);

if(div[1].signum() != 0)

throw new ArithmeticException("Information lost in conversion to BigInteger.");

return div[0];

}

public String toString()

{

return toString(10);

}

public String toString(int base)

{

return n.toString(base) + "/" + d.toString(base);

}

public static BigFraction valueOf(double val)

{

return new BigFraction(BigDecimal.valueOf(val));

}

public static BigFraction valueOf(long val)

{

return valueOf(val, 1);

}

public static BigFraction valueOf(long numerator, long denominator)

{

return new BigFraction(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));

}

public static BigFraction valueOf(String val)

{

try

{

return new BigFraction(val, 10);

}

catch(NumberFormatException e)

{

try

{

return new BigFraction(new BigDecimal(val));

}

catch(NumberFormatException e2)

{

}

}

throw new IllegalArgumentException("Must be passed a properly formatted String.");

}

}

Adeodatus at 2007-7-7 2:28:58 > top of Java-index,Other Topics,Java Community Process (JCP) Program...
# 2

If you're still after comments:

1. Javadoc it.

2. You should be able to improve performance of the multiply and divide methods by taking the GCDs earlier. (a / b) * (c / d) = ((a / gcd(a, d)) * (c / gcd(b, c))) / ((b / gcd(b, c)) * (d / gcd(a, d))).

3. I am somewhat bemused by public BigFraction(String val)

{

this(val, val.indexOf('/'));

}

public BigFraction(String val, int radix)

{

I suspect the first one to be buggy, because the parameter "radix" does seem to be treated as a radix.

YATArchivist at 2007-7-7 2:28:58 > top of Java-index,Other Topics,Java Community Process (JCP) Program...
# 3

> 3. I am somewhat bemused by public

> BigFraction(String val)

>{

> this(val, val.indexOf('/'));

>}

>

>public BigFraction(String val, int radix)

> {

I suspect the first one to be buggy,

> buggy, because the parameter "radix" does seem to be

> treated as a radix.

>.<

Doh.

Adeodatus at 2007-7-7 2:28:58 > top of Java-index,Other Topics,Java Community Process (JCP) Program...