在Java中表示分数的最佳方式是什么?

我正在尝试在Java中使用分数。

我想要实现算术函数。 为此,我首先需要一种方法来规范函数。 我知道我不能添加1/6和1/2,直到我有一个共同的标准。 我将不得不添加1/6和3/6。 一个天真的方法会让我增加2/12和6/12,然后减少。 我如何才能达到性能最低的共同标准? 什么算法最适合这个?


版本8(感谢hstoerr):

改进包括:

  • equals()方法现在与compareTo()方法一致
  • final class Fraction extends Number {
        private int numerator;
        private int denominator;
    
        public Fraction(int numerator, int denominator) {
            if(denominator == 0) {
                throw new IllegalArgumentException("denominator is zero");
            }
            if(denominator < 0) {
                numerator *= -1;
                denominator *= -1;
            }
            this.numerator = numerator;
            this.denominator = denominator;
        }
    
        public Fraction(int numerator) {
            this.numerator = numerator;
            this.denominator = 1;
        }
    
        public int getNumerator() {
            return this.numerator;
        }
    
        public int getDenominator() {
            return this.denominator;
        }
    
        public byte byteValue() {
            return (byte) this.doubleValue();
        }
    
        public double doubleValue() {
            return ((double) numerator)/((double) denominator);
        }
    
        public float floatValue() {
            return (float) this.doubleValue();
        }
    
        public int intValue() {
            return (int) this.doubleValue();
        }
    
        public long longValue() {
            return (long) this.doubleValue();
        }
    
        public short shortValue() {
            return (short) this.doubleValue();
        }
    
        public boolean equals(Fraction frac) {
            return this.compareTo(frac) == 0;
        }
    
        public int compareTo(Fraction frac) {
            long t = this.getNumerator() * frac.getDenominator();
            long f = frac.getNumerator() * this.getDenominator();
            int result = 0;
            if(t>f) {
                result = 1;
            }
            else if(f>t) {
                result = -1;
            }
            return result;
        }
    }
    

    我删除了以前的所有版本。 我感谢:

  • 戴夫雷
  • 克莱图斯
  • duffymo
  • 詹姆士
  • 米尔豪斯
  • 奥斯卡雷耶斯
  • Jason S
  • 弗朗西斯科Canedo
  • 非法程序员
  • Beska

  • 不久之前,我写了一个BigFraction类,用于处理Project Euler问题。 它保留一个BigInteger分子和分母,所以它永远不会溢出。 但是,对于很多操作来说,你知道它永远不会溢出,这对你来说会很慢。无论如何,如果你愿意,也可以使用它。 我一直渴望以某种方式证明这一点。 :)

    编辑 :此代码的最新版本,包括单元测试现在托管在GitHub上,也可以通过Maven Central获得。 我将我的原始代码留在这里,以便这个答案不仅仅是一个链接...


    import java.math.*;
    
    /**
     * 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;
    
      public final static BigFraction ZERO = new BigFraction(BigInteger.ZERO, BigInteger.ONE, true);
      public final static BigFraction ONE = new BigFraction(BigInteger.ONE, BigInteger.ONE, true);
    
      /**
       * 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.");
    
        //only numerator should be negative.
        if(denominator.signum() < 0)
        {
          numerator = numerator.negate();
          denominator = denominator.negate();
        }
    
        //create a reduced fraction
        BigInteger gcd = numerator.gcd(denominator);
        this.numerator = numerator.divide(gcd);
        this.denominator = denominator.divide(gcd);
      }
    
      /**
       * Constructs a BigFraction from a whole number.
       */
      public BigFraction(BigInteger numerator)
      {
        this(numerator, BigInteger.ONE, true);
      }
    
      public BigFraction(long numerator, long denominator)
      {
        this(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
      }
    
      public BigFraction(long numerator)
      {
        this(BigInteger.valueOf(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;
        }
    
        final long bits = Double.doubleToLongBits(d);
        final int sign = (int)(bits >> 63) & 0x1;
        final int exponent = ((int)(bits >> 52) & 0x7ff) - 0x3ff;
        final long mantissa = bits & 0xfffffffffffffL;
    
        //number is (-1)^sign * 2^(exponent) * 1.mantissa
        BigInteger tmpNumerator = BigInteger.valueOf(sign==0 ? 1 : -1);
        BigInteger tmpDenominator = BigInteger.ONE;
    
        //use shortcut: 2^x == 1 << x.  if x is negative, shift the denominator
        if(exponent >= 0)
          tmpNumerator = tmpNumerator.multiply(BigInteger.ONE.shiftLeft(exponent));
        else
          tmpDenominator = tmpDenominator.multiply(BigInteger.ONE.shiftLeft(-exponent));
    
        //1.mantissa == 1 + mantissa/2^52 == (2^52 + mantissa)/2^52
        tmpDenominator = tmpDenominator.multiply(BigInteger.valueOf(0x10000000000000L));
        tmpNumerator = tmpNumerator.multiply(BigInteger.valueOf(0x10000000000000L + mantissa));
    
        BigInteger gcd = tmpNumerator.gcd(tmpDenominator);
        numerator = tmpNumerator.divide(gcd);
        denominator = tmpDenominator.divide(gcd);
      }
    
      /**
       * 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.");
    
        BigFraction tmp = new BigFraction(numerator).divide(new BigFraction(denominator));
        this.numerator = tmp.numerator;
        this.denominator = tmp.denominator;
      }
    
      /**
       * 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()));
      }
    
      public BigFraction(BigDecimal numerator, BigDecimal denominator)
      {
        if(denominator.equals(BigDecimal.ZERO))
          throw new ArithmeticException("Divide by zero.");
    
        BigFraction tmp = new BigFraction(numerator).divide(new BigFraction(denominator));
        this.numerator = tmp.numerator;
        this.denominator = tmp.denominator;
      }
    
      /**
       * 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");
    
        //n1/d1 + n2/d2 = (n1*d2 + d1*n2)/(d1*d2) 
        return new BigFraction(numerator.multiply(f.denominator).add(denominator.multiply(f.numerator)),
                               denominator.multiply(f.denominator));
      }
    
      /**
       * Returns this + b.
       */
      public BigFraction add(BigInteger b)
      {
        if(b == null)
          throw new IllegalArgumentException("Null argument");
    
        //n1/d1 + n2 = (n1 + d1*n2)/d1
        return new BigFraction(numerator.add(denominator.multiply(b)),
                               denominator, true);
      }
    
      /**
       * 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.numerator)),
                               denominator.multiply(f.denominator));
      }
    
      /**
       * Returns this - b.
       */
      public BigFraction subtract(BigInteger b)
      {
        if(b == null)
          throw new IllegalArgumentException("Null argument");
    
        return new BigFraction(numerator.subtract(denominator.multiply(b)),
                               denominator, true);
      }
    
      /**
       * 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");
    
        return new BigFraction(numerator.multiply(f.numerator), denominator.multiply(f.denominator));
      }
    
      /**
       * Returns this * b.
       */
      public BigFraction multiply(BigInteger b)
      {
        if(b == null)
          throw new IllegalArgumentException("Null argument");
    
        return new BigFraction(numerator.multiply(b), denominator);
      }
    
      /**
       * 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");
    
        return new BigFraction(numerator.multiply(f.denominator), denominator.multiply(f.numerator));
      }
    
      /**
       * 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");
    
        return new BigFraction(numerator, denominator.multiply(b));
      }
    
      /**
       * 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");
    
        return new BigFraction(denominator, numerator, true);
      }
    
      /**
       * 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");
    
        //easy case: this and f have different signs
        if(signum() != f.signum())
          return signum() - f.signum();
    
        //next easy case: this and f have the same denominator
        if(denominator.equals(f.denominator))
          return numerator.compareTo(f.numerator);
    
        //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");
    
        return (this.compareTo(f) <= 0 ? this : f);
      }
    
      /**
       * Returns the maximum of this and f.
       */
      public BigFraction max(BigFraction f)
      {
        if(f == null)
          throw new IllegalArgumentException("Null argument");
    
        return (this.compareTo(f) >= 0 ? this : f);
      }
    
      /**
       * Returns a positive BigFraction, greater than or equal to zero, and less than one.
       */
      public static BigFraction random()
      {
        return new BigFraction(Math.random());
      }
    
      public final BigInteger getNumerator() { return numerator; }
      public final BigInteger getDenominator() { return denominator; }
    
      //implementation of Number class.  may cause overflow.
      public byte   byteValue()   { return (byte) Math.max(Byte.MIN_VALUE,    Math.min(Byte.MAX_VALUE,    longValue())); }
      public short  shortValue()  { return (short)Math.max(Short.MIN_VALUE,   Math.min(Short.MAX_VALUE,   longValue())); }
      public int    intValue()    { return (int)  Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, longValue())); }
      public long   longValue()   { return Math.round(doubleValue()); }
      public float  floatValue()  { return (float)doubleValue(); }
      public double doubleValue() { return toBigDecimal(18).doubleValue(); }
    
      /**
       * 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
    
        final BigInteger FIVE = BigInteger.valueOf(5);
        int fives = 0;
        BigInteger[] divMod = null;
    
        //while(tmpDen % 5 == 0) { fives++; tmpDen /= 5; }
        while(BigInteger.ZERO.equals((divMod = tmpDen.divideAndRemainder(FIVE))[1]))
        {
          fives++;
          tmpDen = divMod[0];
        }
    
        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);
    
          if(twos < fives)
            unscaled = unscaled.shiftLeft(fives - twos); //x * 2^n === x << n
          else if (fives < twos)
            unscaled = unscaled.multiply(FIVE.pow(twos - fives));
    
          return new BigDecimal(unscaled, scale);
        }
    
        //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;
        }
      }
    
    }
    

  • 使它不可变;
  • 使其成为规范,意思是6/4变为3/2(最大公约数算法对此有用);
  • 称它为理性的,因为你所代表的是一个有理数;
  • 您可以使用BigInteger来存储任意精确的值。 如果不是那么long ,那实施起来更容易;
  • 使分母总是正面的。 分子应由分子携带;
  • 扩展Number ;
  • 实现Comparable<T> ;
  • 实现equals()hashCode() ;
  • 为由String表示的数字添加工厂方法;
  • 添加一些便利的工厂方法;
  • 添加一个toString() ; 和
  • 使其可Serializable
  • 事实上,请试试这个尺寸。 它运行但可能有一些问题:

    public class BigRational extends Number implements Comparable<BigRational>, Serializable {
        public final static BigRational ZERO = new BigRational(BigInteger.ZERO, BigInteger.ONE);
        private final static long serialVersionUID = 1099377265582986378L;
    
        private final BigInteger numerator, denominator;
    
        private BigRational(BigInteger numerator, BigInteger denominator) {
            this.numerator = numerator;
            this.denominator = denominator;
        }
    
        private static BigRational canonical(BigInteger numerator, BigInteger denominator, boolean checkGcd) {
            if (denominator.signum() == 0) {
                throw new IllegalArgumentException("denominator is zero");
            }
            if (numerator.signum() == 0) {
                return ZERO;
            }
            if (denominator.signum() < 0) {
                numerator = numerator.negate();
                denominator = denominator.negate();
            }
            if (checkGcd) {
                BigInteger gcd = numerator.gcd(denominator);
                if (!gcd.equals(BigInteger.ONE)) {
                    numerator = numerator.divide(gcd);
                    denominator = denominator.divide(gcd);
                }
            }
            return new BigRational(numerator, denominator);
        }
    
        public static BigRational getInstance(BigInteger numerator, BigInteger denominator) {
            return canonical(numerator, denominator, true);
        }
    
        public static BigRational getInstance(long numerator, long denominator) {
            return canonical(new BigInteger("" + numerator), new BigInteger("" + denominator), true);
        }
    
        public static BigRational getInstance(String numerator, String denominator) {
            return canonical(new BigInteger(numerator), new BigInteger(denominator), true);
        }
    
        public static BigRational valueOf(String s) {
            Pattern p = Pattern.compile("(-?d+)(?:.(d+)?)?0*(?:e(-?d+))?");
            Matcher m = p.matcher(s);
            if (!m.matches()) {
                throw new IllegalArgumentException("Unknown format '" + s + "'");
            }
    
            // this translates 23.123e5 to 25,123 / 1000 * 10^5 = 2,512,300 / 1 (GCD)
            String whole = m.group(1);
            String decimal = m.group(2);
            String exponent = m.group(3);
            String n = whole;
    
            // 23.123 => 23123
            if (decimal != null) {
                n += decimal;
            }
            BigInteger numerator = new BigInteger(n);
    
            // exponent is an int because BigInteger.pow() takes an int argument
            // it gets more difficult if exponent needs to be outside {-2 billion,2 billion}
            int exp = exponent == null ? 0 : Integer.valueOf(exponent);
            int decimalPlaces = decimal == null ? 0 : decimal.length();
            exp -= decimalPlaces;
            BigInteger denominator;
            if (exp < 0) {
                denominator = BigInteger.TEN.pow(-exp);
            } else {
                numerator = numerator.multiply(BigInteger.TEN.pow(exp));
                denominator = BigInteger.ONE;
            }
    
            // done
            return canonical(numerator, denominator, true);
        }
    
        // Comparable
        public int compareTo(BigRational o) {
            // note: this is a bit of cheat, relying on BigInteger.compareTo() returning
            // -1, 0 or 1.  For the more general contract of compareTo(), you'd need to do
            // more checking
            if (numerator.signum() != o.numerator.signum()) {
                return numerator.signum() - o.numerator.signum();
            } else {
                // oddly BigInteger has gcd() but no lcm()
                BigInteger i1 = numerator.multiply(o.denominator);
                BigInteger i2 = o.numerator.multiply(denominator);
                return i1.compareTo(i2); // expensive!
            }
        }
    
        public BigRational add(BigRational o) {
            if (o.numerator.signum() == 0) {
                return this;
            } else if (numerator.signum() == 0) {
                return o;
            } else if (denominator.equals(o.denominator)) {
                return new BigRational(numerator.add(o.numerator), denominator);
            } else {
                return canonical(numerator.multiply(o.denominator).add(o.numerator.multiply(denominator)), denominator.multiply(o.denominator), true);
            }
        }
    
    
        public BigRational multiply(BigRational o) {
            if (numerator.signum() == 0 || o.numerator.signum( )== 0) {
                return ZERO;
            } else if (numerator.equals(o.denominator)) {
                return canonical(o.numerator, denominator, true);
            } else if (o.numerator.equals(denominator)) {
                return canonical(numerator, o.denominator, true);
            } else if (numerator.negate().equals(o.denominator)) {
                return canonical(o.numerator.negate(), denominator, true);
            } else if (o.numerator.negate().equals(denominator)) {
                return canonical(numerator.negate(), o.denominator, true);
            } else {
                return canonical(numerator.multiply(o.numerator), denominator.multiply(o.denominator), true);
            }
        }
    
        public BigInteger getNumerator() { return numerator; }
        public BigInteger getDenominator() { return denominator; }
        public boolean isInteger() { return numerator.signum() == 0 || denominator.equals(BigInteger.ONE); }
        public BigRational negate() { return new BigRational(numerator.negate(), denominator); }
        public BigRational invert() { return canonical(denominator, numerator, false); }
        public BigRational abs() { return numerator.signum() < 0 ? negate() : this; }
        public BigRational pow(int exp) { return canonical(numerator.pow(exp), denominator.pow(exp), true); }
        public BigRational subtract(BigRational o) { return add(o.negate()); }
        public BigRational divide(BigRational o) { return multiply(o.invert()); }
        public BigRational min(BigRational o) { return compareTo(o) <= 0 ? this : o; }
        public BigRational max(BigRational o) { return compareTo(o) >= 0 ? this : o; }
    
        public BigDecimal toBigDecimal(int scale, RoundingMode roundingMode) {
            return isInteger() ? new BigDecimal(numerator) : new BigDecimal(numerator).divide(new BigDecimal(denominator), scale, roundingMode);
        }
    
        // Number
        public int intValue() { return isInteger() ? numerator.intValue() : numerator.divide(denominator).intValue(); }
        public long longValue() { return isInteger() ? numerator.longValue() : numerator.divide(denominator).longValue(); }
        public float floatValue() { return (float)doubleValue(); }
        public double doubleValue() { return isInteger() ? numerator.doubleValue() : numerator.doubleValue() / denominator.doubleValue(); }
    
        @Override
        public String toString() { return isInteger() ? String.format("%,d", numerator) : String.format("%,d / %,d", numerator, denominator); }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            BigRational that = (BigRational) o;
    
            if (denominator != null ? !denominator.equals(that.denominator) : that.denominator != null) return false;
            if (numerator != null ? !numerator.equals(that.numerator) : that.numerator != null) return false;
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int result = numerator != null ? numerator.hashCode() : 0;
            result = 31 * result + (denominator != null ? denominator.hashCode() : 0);
            return result;
        }
    
        public static void main(String args[]) {
            BigRational r1 = BigRational.valueOf("3.14e4");
            BigRational r2 = BigRational.getInstance(111, 7);
            dump("r1", r1);
            dump("r2", r2);
            dump("r1 + r2", r1.add(r2));
            dump("r1 - r2", r1.subtract(r2));
            dump("r1 * r2", r1.multiply(r2));
            dump("r1 / r2", r1.divide(r2));
            dump("r2 ^ 2", r2.pow(2));
        }
    
        public static void dump(String name, BigRational r) {
            System.out.printf("%s = %s%n", name, r);
            System.out.printf("%s.negate() = %s%n", name, r.negate());
            System.out.printf("%s.invert() = %s%n", name, r.invert());
            System.out.printf("%s.intValue() = %,d%n", name, r.intValue());
            System.out.printf("%s.longValue() = %,d%n", name, r.longValue());
            System.out.printf("%s.floatValue() = %,f%n", name, r.floatValue());
            System.out.printf("%s.doubleValue() = %,f%n", name, r.doubleValue());
            System.out.println();
        }
    }
    

    输出是:

    r1 = 31,400
    r1.negate() = -31,400
    r1.invert() = 1 / 31,400
    r1.intValue() = 31,400
    r1.longValue() = 31,400
    r1.floatValue() = 31,400.000000
    r1.doubleValue() = 31,400.000000
    
    r2 = 111 / 7
    r2.negate() = -111 / 7
    r2.invert() = 7 / 111
    r2.intValue() = 15
    r2.longValue() = 15
    r2.floatValue() = 15.857142
    r2.doubleValue() = 15.857143
    
    r1 + r2 = 219,911 / 7
    r1 + r2.negate() = -219,911 / 7
    r1 + r2.invert() = 7 / 219,911
    r1 + r2.intValue() = 31,415
    r1 + r2.longValue() = 31,415
    r1 + r2.floatValue() = 31,415.857422
    r1 + r2.doubleValue() = 31,415.857143
    
    r1 - r2 = 219,689 / 7
    r1 - r2.negate() = -219,689 / 7
    r1 - r2.invert() = 7 / 219,689
    r1 - r2.intValue() = 31,384
    r1 - r2.longValue() = 31,384
    r1 - r2.floatValue() = 31,384.142578
    r1 - r2.doubleValue() = 31,384.142857
    
    r1 * r2 = 3,485,400 / 7
    r1 * r2.negate() = -3,485,400 / 7
    r1 * r2.invert() = 7 / 3,485,400
    r1 * r2.intValue() = 497,914
    r1 * r2.longValue() = 497,914
    r1 * r2.floatValue() = 497,914.281250
    r1 * r2.doubleValue() = 497,914.285714
    
    r1 / r2 = 219,800 / 111
    r1 / r2.negate() = -219,800 / 111
    r1 / r2.invert() = 111 / 219,800
    r1 / r2.intValue() = 1,980
    r1 / r2.longValue() = 1,980
    r1 / r2.floatValue() = 1,980.180176
    r1 / r2.doubleValue() = 1,980.180180
    
    r2 ^ 2 = 12,321 / 49
    r2 ^ 2.negate() = -12,321 / 49
    r2 ^ 2.invert() = 49 / 12,321
    r2 ^ 2.intValue() = 251
    r2 ^ 2.longValue() = 251
    r2 ^ 2.floatValue() = 251.448975
    r2 ^ 2.doubleValue() = 251.448980
    

    我正在尝试在Java中使用适当的分数。

    Apache Commons Math已经有一段时间了。 大多数情况下,答案是:“我希望Java在核心库中有类似X的东西!” 可以在Apache Commons库的保护下找到。

    链接地址: http://www.djcxy.com/p/76199.html

    上一篇: Best way to represent a fraction in Java?

    下一篇: Can't create a runnable jar with Intellij Idea