Forum Stats

  • 3,837,210 Users
  • 2,262,237 Discussions
  • 7,900,223 Comments

Discussions

Code Review

843853
843853 Member Posts: 3,739
edited Feb 6, 2005 6:17PM in Java Programming
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.*;
   public class BigFraction extends Number implements Comparable<BigFraction>
   {
      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 final BigDecimal m;
      
      private static final BigInteger TWO = BigInteger.valueOf(2L);
      private static final java.security.SecureRandom rng;
      static
      {
         rng = new java.security.SecureRandom();
         rng.setSeed(BigInteger.valueOf(System.currentTimeMillis() * 1000000L /*+ 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)
            throw new 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))
            throw new 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)
            return this;
         return new BigFraction(n.abs(), d.abs());
      }
      public BigFraction add(BigFraction augend)
      {
         return new BigFraction(n.multiply(augend.d).add(augend.n.multiply(d)), d.multiply(augend.d)).simp();
      }
      public byte byteValueExact()
      {
         return m.byteValueExact();
      }
      public byte byteValue()
      {
         return m.byteValue();
      }
      public int compareTo(BigFraction val)
      {
         int   s = 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)
      {
         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(n.divide(d), BigInteger.ONE);
      }
      public double doubleValue()
      {
         return m.doubleValue();
      }
      public boolean equals(Object o)
      {
         return o instanceof BigFraction ? equals((BigFraction) o) : false;
      }
      public boolean equals(BigFraction val)
      {
         if(this == val)
            return true;
         BigFraction a = this.simplify(),
                     b = val.simplify();
         return a.d.equals(b.d) && a.n.equals(b.n);
      }
      public float floatValue() 
      {
         return m.floatValue();
      }
      public int hashCode() 
      {
         return n.hashCode()+d.hashCode();
      }
      public int intValueExact()
      {
         return m.intValueExact();
      }
      public int intValue()
      {
         return m.intValue();
      }
      public BigFraction inverse()
      {
         return new BigFraction(d, n);
      }
      public boolean isSimplified()
      {
         return (d.signum() == 0) && n.gcd(d).equals(BigInteger.ONE);
      }
      public long longValueExact() 
      {
         return m.longValueExact();
      }
      public long 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) 
      {
         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)
      {
         return new BigFraction(new BigInteger(bits, rng), TWO.pow(bits));
      }
      public BigFraction remainder(BigFraction divisor)
      {
         return subtract(divisor.multiply(divideToIntegralValue(divisor)));
      }
      public short shortValueExact()
      {
         return m.shortValueExact();
      }
      public short shortValue()
      {
         return m.shortValue();
      }
      public int 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();
            }
         }
         return this;
      }
      public BigFraction simplify()
      {
         BigInteger gcd = n.gcd(d);
         if(d.signum() == 1)
         {
            if(gcd.equals(BigInteger.ONE))
               return this;
            return new BigFraction(n.divide(gcd), d.divide(gcd));
         }
         else
            return new BigFraction(n.divide(gcd).negate(), d.divide(gcd).negate());
      }
      public BigFraction subtract(BigFraction subtrahend)
      {
         return new 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)
      {
         return new 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))
            throw new 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);
      }
      public static BigFraction valueOf(double val)
      {
         return new BigFraction(BigDecimal.valueOf(val));
      }
      public static BigFraction valueOf(long val)
      {
         return valueOf(val, 1L);
      }
      public static BigFraction valueOf(long numerator, long denominator)
      {
         return new BigFraction(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
      }
      public static BigFraction valueOf(String val)
      {
         int index = val.indexOf('/');
         if(index >= 0)
            return new BigFraction(val);
         else
            return new BigFraction(new BigDecimal(val));
      }
   }

Comments

  • 843853
    843853 Member Posts: 3,739
    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(2L);
          private static final java.security.SecureRandom rng;
          static
          {
             rng = new java.security.SecureRandom();
             rng.setSeed(BigInteger.valueOf(System.currentTimeMillis() * 1000000L + System.nanoTime()).pow(65).subtract(BigInteger.valueOf(13L)).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, 1L);
          }
          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.");
          }
       }
  • 843853
    843853 Member Posts: 3,739
    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.
This discussion has been closed.