วิธีที่ดีที่สุดในการแทนเศษส่วนใน Java?


100

ฉันพยายามทำงานกับเศษส่วนใน Java

ฉันต้องการใช้ฟังก์ชันเลขคณิต สำหรับสิ่งนี้ก่อนอื่นฉันจะต้องใช้วิธีทำให้ฟังก์ชันปกติ ฉันรู้ว่าฉันไม่สามารถเพิ่ม 1/6 และ 1/2 ได้จนกว่าฉันจะมีตัวส่วนร่วม ฉันจะต้องเพิ่ม 1/6 และ 3/6 วิธีการที่ไร้เดียงสาจะทำให้ฉันเพิ่ม 2/12 และ 6/12 แล้วลด ฉันจะได้ตัวส่วนร่วมที่มีโทษประสิทธิภาพน้อยที่สุดได้อย่างไร อัลกอริทึมใดดีที่สุดสำหรับสิ่งนี้


เวอร์ชัน 8 (ขอบคุณhstoerr ):

การปรับปรุง ได้แก่ :

  • ขณะนี้วิธีการเท่ากับ () สอดคล้องกับวิธีการ 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;
    }
}

ฉันได้ลบเวอร์ชันก่อนหน้านี้ทั้งหมดแล้ว ขอขอบคุณ:


34
ทิ้งรหัสใช้ Apache Commons :) commons.apache.org/math/userguide/fraction.html
Patrick

3
ความคิดเห็นของ Patrick สมควรได้รับ +1 หากโพสต์เป็นคำตอบ ในกรณีส่วนใหญ่นั่นคือคำตอบที่ถูกต้อง "รู้จักและใช้ไลบรารี" ตามที่ Effective Java กล่าว คำถามเดิมนั้นชัดเจนและมีประโยชน์เช่นกัน
Jonik

สังเกตเห็นว่าคุณยอมรับคำตอบของฉัน .. หากคุณใช้รหัสนั้นจริงและพบปัญหาเกี่ยวกับรหัสนั้นหรือสิ่งใดที่ขาดโปรดแจ้งให้เราทราบ! ส่งอีเมลถึงฉันจากเว็บไซต์ของฉัน: vacant-nebula.com/contact/kip
กีบ

ฉันขอแนะนำให้คุณแก้ไขเมธอด "CompareTo" ของคุณและส่ง "this.getNumerator ()" ให้ยาวก่อนการคูณ มิฉะนั้นโค้ดจะยังคงล้น ฉันคิดว่ามันเป็นการดีที่จะใช้ <Fraction> เปรียบเทียบเนื่องจากคุณได้ใช้เมธอด CompareTo แล้ว
Hosam Aly

และเนื่องจากคุณได้ไปไกลแล้วการใช้ equals และ hashCode ก็อาจมีประโยชน์เช่นกัน
Hosam Aly

คำตอบ:


66

มันเกิดขึ้นเพียงเพื่อที่ผมเขียนระดับ BigFraction ไม่นานที่ผ่านมาสำหรับปัญหาโครงการออยเลอร์ มันเก็บตัวเศษและตัวส่วนของ BigInteger ไว้ดังนั้นมันจะไม่มีวันล้น แต่มันจะช้าไปหน่อยสำหรับการดำเนินการจำนวนมากที่คุณรู้ว่าจะไม่มีวันล้น .. อย่างไรก็ตามให้ใช้หากคุณต้องการ ฉันอยากจะอวดสิ่งนี้ออกไป :)

แก้ไข : รุ่นล่าสุดและยิ่งใหญ่ที่สุดของรหัสนี้รวมทั้งการทดสอบหน่วยเป็นเจ้าภาพในขณะนี้บน GitHubและยังสามารถใช้ได้ผ่านทาง Maven กลาง ฉันทิ้งรหัสเดิมไว้ที่นี่เพื่อไม่ให้คำตอบนี้เป็นเพียงลิงค์ ...


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;
    }
  }

}

ถ้าอาร์กิวเมนต์เป็นโมฆะให้โยน NullPointerException ในความเป็นจริงรหัสจะทำเช่นนั้นดังนั้นเช็คของคุณ (และแทนที่ด้วย IllegalArgumentException (เป็นการขยายโค้ดที่ไม่จำเป็น
cletus

25
ฉันไม่เห็นด้วย; หากผู้ใช้รายอื่นใช้คลาสนี้โดยไม่ได้ดูที่มาของฉันและได้รับ NullPointerException เขาจะคิดว่ามีข้อบกพร่องในโค้ดของฉัน แต่ IllegalArgumentException แสดงให้เห็นว่าเขาได้ละเมิดสัญญาโดยนัยโดย javadoc (แม้ว่าฉันจะไม่ได้ระบุอย่างชัดเจนก็ตาม)
กีบ


1
แค่ถามว่า Fraction และ BigFraction ใน Commons Math มีอะไรผิดปกติ?
Mortimer

@Mortimer: ไม่แน่ใจฉันไม่เคยดู
Kip

61
  • ทำให้ไม่เปลี่ยนรูป ;
  • ทำให้เป็นที่ยอมรับหมายถึง 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

31

ฉันพยายามทำงานกับเศษส่วนที่เหมาะสมใน Java

Apache Commons MathมีคลาสFractionมาระยะหนึ่งแล้ว บ่อยครั้งที่คำตอบ "เด็กชายฉันอยากให้ Java มีXในไลบรารีหลัก สามารถพบได้ภายใต้ร่มของห้องสมุด Apache คอมมอนส์


2
ฉันจะบอกคุณว่าทำไมสิ่งนี้ถึงต่ำมากห้องสมุด Apache Commons ไม่เหมาะสำหรับมือใหม่ อันดับแรกไม่มีลิงก์โดยตรงสำหรับดาวน์โหลดในหน้านั้น (ซ่อนอยู่ในเมนูแถบด้านข้าง) ประการที่สองไม่มีคำแนะนำในการใช้งาน (การเพิ่ม jar ในเส้นทางการสร้างของคุณ) ประการที่สามฉันได้รับข้อผิดพลาด classDefNotFound หลังจากเพิ่มทั้งหมดแล้ว . ดังนั้นคุณจึงไม่ได้รับการโหวตจากเราคนที่รู้วิธีคัดลอกและวางเท่านั้น
Noumenon

@Noumenon วิธีการใช้ build manager (เช่น maven) และเพิ่มการพึ่งพาใน POM ได้อย่างไร?
eugene.polschikov

1
ฉันต้องการดูข้อความแจ้ง "วิธีใช้สิ่งนี้ในโครงการของคุณ" เล็กน้อยสำหรับ noobs ข้อเสนอแนะนั้นสามารถไปที่นั่นได้ ที่กล่าวว่าฉันได้หาวิธีทำและใช้ในแอปโรงงานของฉันที่ต้องแสดงเศษส่วนของนิ้วและฉันไม่เคยกลับมาให้คะแนนโหวตของคุณ ขอบคุณมากที่นี่เป็นไปอย่างล่าช้า
Noumenon

นั่นเป็นข้อเสนอแนะที่ยุติธรรม นี่คือคำขอบคุณของฉันที่ล่าช้าเช่นกัน! :)
yawmark

อันนี้ค่อนข้างใช้งานง่าย
Eric Wang

24

กรุณาทำให้เป็นประเภทที่ไม่เปลี่ยนรูป! ค่าของเศษส่วนไม่เปลี่ยนแปลงตัวอย่างเช่นครึ่งหนึ่งไม่กลายเป็นสาม แทนที่จะเป็น setDenominator คุณสามารถมี withDenominator ซึ่งส่งคืนเศษส่วนใหม่ซึ่งมีตัวเศษเหมือนกัน แต่เป็นตัวหารที่ระบุ

ชีวิตง่ายขึ้นมากด้วยประเภทที่ไม่เปลี่ยนรูป

การลบล้างค่าเท่ากับและแฮชโค้ดก็สมเหตุสมผลเช่นกันดังนั้นจึงสามารถใช้ในแผนที่และชุดได้ ประเด็นของ Outlaw Programmer เกี่ยวกับตัวดำเนินการทางคณิตศาสตร์และการจัดรูปแบบสตริงก็ดีเช่นกัน

ตามคำแนะนำทั่วไปให้ดูที่ BigInteger และ BigDecimal พวกเขาไม่ได้ทำสิ่งเดียวกัน แต่คล้ายกันมากพอที่จะให้ความคิดดีๆแก่คุณ


5
"โปรดทำให้มันเป็นประเภทที่ไม่เปลี่ยนรูป! ค่าของเศษส่วนไม่เปลี่ยนแปลง - ครึ่งหนึ่งไม่กลายเป็นสามเช่น" รายการ / ทูเปิล / เวกเตอร์ (1, 2, 3, 4) ไม่กลายเป็นค่า (4, 3, 2, 1) แต่ดูเหมือนจะไม่รบกวนคนส่วนใหญ่ที่แสดงรายการเปลี่ยนสถานะ ไม่ใช่ว่าฉันไม่เห็นด้วยกับความไม่เปลี่ยนรูปของเศษส่วน แต่มันก็สมควรได้รับการโต้แย้งที่ดีกว่า รู้สึกเหมือนมีค่ามากกว่ากลุ่มรัฐ ความคาดหวังของโปรแกรมเมอร์เป็นเหตุผลที่ถูกต้องหรือไม่? ฉันไม่แน่ใจ 100% แต่ดูเหมือนเป็นความคิดที่ดี
Jonas Kölker

2
ทั้งในชีวิตจริงรายการทำการเปลี่ยนแปลง: วิธีการทำคุณเขียนรายการช้อปปิ้ง? คุณเริ่มต้นด้วยกระดาษเปล่าแล้วเขียนลงไป ครึ่งทางคุณยังคงเรียกมันว่า "รายการช้อปปิ้ง" ต้องบอกว่าการเขียนโปรแกรมเชิงฟังก์ชันพยายามทำให้รายการไม่เปลี่ยนรูปด้วยซ้ำ ...
Jon Skeet

7

อย่างหนึ่งฉันจะกำจัดเซตเทอร์และทำให้เศษส่วนไม่เปลี่ยนรูป

คุณอาจต้องการวิธีการบวกลบและอื่น ๆ และอาจเป็นวิธีที่จะได้รับการแสดงในรูปแบบ String ต่างๆ

แก้ไข: ฉันอาจจะทำเครื่องหมายฟิลด์เป็น 'สุดท้าย' เพื่อแสดงเจตนาของฉัน แต่ฉันคิดว่ามันไม่ใช่เรื่องใหญ่ ...


2
ฉันสงสัยว่าจะมีคำตอบที่ "ทำให้ไม่เปลี่ยนรูป" กี่คำตอบ :)
Jon Skeet

5
  • มันไม่มีจุดหมายหากไม่มีวิธีการทางคณิตศาสตร์เช่นการบวก () และคูณ () เป็นต้น
  • แน่นอนคุณควรแทนที่เท่ากับ () และ hashCode ()
  • คุณควรเพิ่มวิธีการเพื่อทำให้เศษส่วนเป็นปกติหรือทำโดยอัตโนมัติ ลองคิดดูว่าคุณต้องการให้ 1/2 และ 2/4 เหมือนกันหรือไม่ซึ่งมีผลต่อวิธีการ equals (), hashCode () และ CompareTo ()

5

ฉันจะต้องเรียงลำดับจากน้อยที่สุดไปหามากที่สุดดังนั้นในที่สุดฉันจะต้องแทนค่าเหล่านี้เป็นสองเท่าด้วย

ไม่จำเป็นอย่างเคร่งครัด (ในความเป็นจริงถ้าคุณต้องการจัดการความเท่าเทียมกันอย่างถูกต้องอย่าพึ่งพา double เพื่อให้ทำงานได้อย่างถูกต้อง) ถ้า b * d เป็นบวก a / b <c / d ถ้า ad <bc. หากมีจำนวนเต็มลบเกี่ยวข้องก็สามารถจัดการได้อย่างเหมาะสม ...

ฉันอาจเขียนใหม่เป็น:

public int compareTo(Fraction frac)
{
    // we are comparing this=a/b with frac=c/d 
    // by multiplying both sides by bd.
    // If bd is positive, then a/b < c/d <=> ad < bc.
    // If bd is negative, then a/b < c/d <=> ad > bc.
    // If bd is 0, then you've got other problems (either b=0 or d=0)
    int d = frac.getDenominator();
    long ad = (long)this.numerator * d;
    long bc = (long)this.denominator * frac.getNumerator();
    long diff = ((long)d*this.denominator > 0) ? (ad-bc) : (bc-ad);
    return (diff > 0 ? 1 : (diff < 0 ? -1 : 0));
}

การใช้งานของlongที่นี่คือการจัดให้มีไม่ล้นถ้าคุณคูณสองขนาดใหญ่ints จัดการถ้าคุณสามารถรับประกันได้ว่าตัวส่วนไม่เป็นค่าลบเสมอ (ถ้าเป็นค่าลบให้ลบทั้งตัวเศษและตัวส่วน) จากนั้นคุณสามารถกำจัดได้โดยต้องตรวจสอบว่า b * d เป็นค่าบวกหรือไม่และบันทึกสองสามขั้นตอน ฉันไม่แน่ใจว่าคุณกำลังมองหาพฤติกรรมใดโดยใช้ตัวหารศูนย์

ไม่แน่ใจว่าประสิทธิภาพเปรียบเทียบกับการใช้คู่เพื่อเปรียบเทียบอย่างไร (นั่นคือถ้าคุณสนใจเรื่องประสิทธิภาพมากขนาดนั้น) นี่คือวิธีทดสอบที่ฉันใช้ตรวจสอบ (ดูเหมือนจะทำงานได้อย่างถูกต้อง)

public static void main(String[] args)
{
    int a = Integer.parseInt(args[0]);
    int b = Integer.parseInt(args[1]);
    int c = Integer.parseInt(args[2]);
    int d = Integer.parseInt(args[3]);
    Fraction f1 = new Fraction(a,b); 
    Fraction f2 = new Fraction(c,d);
    int rel = f1.compareTo(f2);
    String relstr = "<=>";
    System.out.println(a+"/"+b+" "+relstr.charAt(rel+1)+" "+c+"/"+d);
}

(ปล. คุณอาจพิจารณาปรับโครงสร้างเพื่อนำไปใช้ComparableหรือComparatorสำหรับชั้นเรียนของคุณ)


นี่ไม่เป็นความจริงถ้าตัวอย่างเช่น a = 1, b = 3, c = -2, d = -3 ถ้า b และ d เป็นค่าบวกแสดงว่า a / b <c / d ถ้าและเฉพาะเมื่อโฆษณา <bc
Luke Woodward

อ๊ะฉันมีคุณสมบัติไม่ถูกต้อง (ขอบคุณ!) เงื่อนไขควรเป็นถ้า bd> 0.
Jason S

จริง. อย่างแม่นยำยิ่งขึ้น a / b <c / d <=> ac <bd เป็นจริงให้ bd> 0. ถ้า bd <0 การสนทนาจะเป็นจริง (ถ้า bd = 0 แสดงว่าคุณมีเศษส่วน :-))
Paul Brinkley

ปิด. คุณหมายถึง a / b <c / d <=> ad <bc for bd> 0 (ฉันเข้าใจถูกเป็นครั้งแรกในความคิดเห็นเกี่ยวกับรหัส!)
Jason S

4

การปรับปรุงเล็กน้อยอย่างหนึ่งอาจเป็นการบันทึกค่าสองเท่าที่คุณกำลังคำนวณเพื่อให้คุณคำนวณได้ในการเข้าถึงครั้งแรกเท่านั้น นี่จะไม่ใช่ชัยชนะครั้งใหญ่เว้นแต่คุณจะเข้าถึงหมายเลขนี้เป็นจำนวนมาก แต่ก็ไม่ยากเกินไปที่จะทำเช่นกัน

อีกจุดหนึ่งอาจเป็นข้อผิดพลาดในการตรวจสอบที่คุณทำในตัวส่วน ... คุณเปลี่ยน 0 เป็น 1 โดยอัตโนมัติไม่แน่ใจว่าสิ่งนี้ถูกต้องสำหรับแอปพลิเคชันเฉพาะของคุณหรือไม่ แต่โดยทั่วไปหากมีคนพยายามหารด้วย 0 มีบางอย่างผิดปกติ . ฉันจะปล่อยให้สิ่งนี้เกิดข้อยกเว้น (ข้อยกเว้นพิเศษหากคุณรู้สึกว่าจำเป็น) แทนที่จะเปลี่ยนค่าด้วยวิธีที่ดูเหมือนจะเป็นไปตามอำเภอใจที่ผู้ใช้ไม่รู้จัก

ในข้อ จำกัด กับความคิดเห็นอื่น ๆ เกี่ยวกับการเพิ่มวิธีการบวกลบ ฯลฯ ... เนื่องจากคุณไม่ได้ระบุว่าต้องการพวกเขาฉันถือว่าคุณไม่ทำ และเว้นแต่คุณจะสร้างห้องสมุดที่จะใช้งานได้จริงในหลาย ๆ ที่หรือโดยคนอื่นให้ไปกับ YAGNI (คุณจะไม่ต้องการมันดังนั้นจึงไม่ควรอยู่ที่นั่น)


ความจริงที่ว่าเขามี getNumerator () และ getDenominator () ทำให้ฉันเชื่อว่าเขากำลังสร้างเศษส่วนใหม่นอกชั้นเรียนนี้ ตรรกะนั้นอาจอยู่ที่นี่ถ้ามีอยู่
Outlaw Programmer

+1 การเปลี่ยน 0 เป็น 1 อย่างเงียบ ๆ ในตัวส่วนเป็นสูตรสำหรับภัยพิบัติ
maaartinus

4

มีหลายวิธีในการปรับปรุงประเภทนี้หรือค่าใด ๆ :

  • ทำให้ชั้นเรียนของคุณไม่เปลี่ยนรูปรวมถึงการทำให้ตัวเศษและตัวส่วนเป็นขั้นสุดท้าย
  • แปลงเศษส่วนเป็นรูปแบบบัญญัติโดยอัตโนมัติเช่น 2/4 -> 1/2
  • ใช้ toString ()
  • ใช้ "public static Fraction valueOf (String s)" เพื่อแปลงจากสตริงเป็นเศษส่วน ใช้วิธีการโรงงานที่คล้ายกันสำหรับการแปลงจาก int, double และอื่น ๆ
  • ใช้การเพิ่มการคูณ ฯลฯ
  • เพิ่มตัวสร้างจากจำนวนเต็ม
  • แทนที่เท่ากับ / hashCode
  • พิจารณาทำให้ Fraction เป็นอินเทอร์เฟซด้วยการใช้งานที่เปลี่ยนไปใช้ BigInteger ตามความจำเป็น
  • พิจารณาการแบ่งประเภทย่อย Number
  • พิจารณารวมค่าคงที่ตั้งชื่อสำหรับค่าทั่วไปเช่น 0 และ 1
  • พิจารณาทำให้เป็นอนุกรม
  • ทดสอบการหารด้วยศูนย์
  • จัดทำเอกสาร API ของคุณ

โดยพื้นฐานแล้วให้ดูที่ API สำหรับคลาสค่าอื่น ๆ เช่นDouble , Integer และทำในสิ่งที่พวกเขาทำ :)


3

ถ้าคุณคูณตัวเศษและตัวส่วนของเศษส่วนหนึ่งกับตัวส่วนของอีกตัวหนึ่งและในทางกลับกันคุณจะได้เศษส่วนสองตัว (ซึ่งยังคงเป็นค่าเดิม) ด้วยตัวส่วนเดียวกันและคุณสามารถเปรียบเทียบตัวเศษได้โดยตรง ดังนั้นคุณไม่จำเป็นต้องคำนวณค่าสองเท่า:

public int compareTo(Fraction frac) {
    int t = this.numerator * frac.getDenominator();
    int f = frac.getNumerator() * this.denominator;
    if(t>f) return 1;
    if(f>t) return -1;
    return 0;
}

สิ่งนี้จะล้มเหลวหาก frac.getDenominator () และ this.denominator มีสัญญาณตรงกันข้าม (ดูโพสต์ของฉัน) นอกจากนี้คุณต้องระวังความจริงที่ว่าการคูณสามารถล้น
Jason S

อ่าใช่จริง แต่ในกรณีนั้นฉันชอบการใช้งานของ Kip ซึ่งอย่างน้อยฉันก็เข้าใจได้ ;)
Francisco Canedo

ฉันจะชี้ให้เห็นว่าในการใช้งานของฉันมีเพียงตัวเศษเท่านั้นที่สามารถเป็นลบได้ ฉันยังใช้ BigIntegers ดังนั้นจะไม่มีทางล้น (แน่นอนว่าต้องเสียค่าใช้จ่ายในการทำงานบางส่วน)
กีบ

2

ฉันจะปรับปรุงรหัสนั้นได้อย่างไร:

  1. ตัวสร้างจาก String Fraction (String s) // คาดหวัง "number / number"
  2. ตัวสร้างสำเนา Fraction (Fraction copy)
  3. แทนที่วิธีการโคลน
  4. ใช้วิธีการเท่ากับ toString และ hashcode
  5. ใช้อินเทอร์เฟซ java.io แบบอนุกรมเทียบเคียงได้
  6. เมธอด "double getDoubleValue ()"
  7. วิธีการเพิ่ม / หาร / ฯลฯ ...
  8. ฉันจะทำให้คลาสนั้นไม่เปลี่ยนรูป (ไม่มีตัวตั้งค่า)

รายการที่ดีงาม อาจไม่จำเป็นต้องโคลน / ต่ออนุกรม แต่อย่างอื่นสมเหตุสมผล
โปรแกรมเมอร์นอกกฎหมาย

@OutlawProgrammer: ใช่ 8 หรือ 3 Cloneable ไม่เปลี่ยนรูปเป็นสิ่งที่ไม่รู้สึก
maaartinus

2

คุณมีฟังก์ชัน CompareTo อยู่แล้ว ... ฉันจะใช้อินเทอร์เฟซเปรียบเทียบ

อาจไม่สำคัญกับสิ่งที่คุณจะทำกับมันจริงๆ



2

โดยเฉพาะ : มีวิธีที่ดีกว่าในการจัดการกับการส่งผ่านตัวส่วนศูนย์หรือไม่? การตั้งค่าตัวส่วนเป็น 1 ให้ความรู้สึกที่ยิ่งใหญ่โดยพลการ ฉันจะทำสิ่งนี้ได้อย่างไร?

ฉันจะบอกว่าโยน ArithmeticException เพื่อหารด้วยศูนย์เพราะนั่นคือสิ่งที่เกิดขึ้นจริง:

public Fraction(int numerator, int denominator) {
    if(denominator == 0)
        throw new ArithmeticException("Divide by zero.");
    this.numerator = numerator;
    this.denominator = denominator;
}

แทนที่จะเป็น "หารด้วยศูนย์" คุณอาจต้องการให้ข้อความระบุว่า "หารด้วยศูนย์: ตัวหารสำหรับเศษส่วนเป็นศูนย์"


1

เมื่อคุณสร้างวัตถุเศษส่วนแล้วทำไมคุณถึงต้องการอนุญาตให้วัตถุอื่นตั้งค่าตัวเศษหรือตัวส่วน? ฉันคิดว่าสิ่งเหล่านี้ควรอ่านเท่านั้น มันทำให้วัตถุไม่เปลี่ยนรูป ...

นอกจากนี้ ... การตั้งค่าตัวส่วนเป็นศูนย์ควรทำให้เกิดข้อยกเว้นอาร์กิวเมนต์ที่ไม่ถูกต้อง (ฉันไม่รู้ว่ามันคืออะไรใน Java)


หรือโยน ArithmeticException ใหม่ ("หารด้วยศูนย์")
กีบ

1

Timothy Budd มีการนำคลาส Rational มาใช้อย่างดีใน "โครงสร้างข้อมูลใน C ++" แน่นอนว่าภาษาต่างกัน แต่พอร์ตไปยัง Java เป็นอย่างดี

ฉันขอแนะนำผู้สร้างเพิ่มเติม ตัวสร้างเริ่มต้นจะมีตัวเลข 0 ตัวส่วน 1 ตัวสร้างอาร์กิวเมนต์เดียวจะถือว่าตัวส่วนเป็น 1 คิดว่าผู้ใช้ของคุณจะใช้คลาสนี้ได้อย่างไร

ไม่มีการตรวจสอบตัวหารศูนย์? การเขียนโปรแกรมตามสัญญาจะให้คุณเพิ่มเข้าไป


1

ฉันจะเป็นคนที่สามหรือห้าหรืออะไรก็ได้ที่แนะนำเพื่อทำให้เศษส่วนของคุณไม่เปลี่ยนรูป ฉันขอแนะนำให้คุณขยายคลาสNumber ฉันอาจดูคลาสDoubleเนื่องจากคุณอาจต้องการใช้วิธีการเดียวกันหลายวิธี

คุณควรใช้เปรียบเทียบและต่อเนื่องได้เนื่องจากอาจเกิดพฤติกรรมนี้ได้ ดังนั้นคุณจะต้องใช้ CompareTo () คุณจะต้องลบล้างเท่ากับ () และฉันไม่สามารถเน้นหนักพอที่คุณจะแทนที่ hashCode () นี่อาจเป็นหนึ่งในไม่กี่กรณีที่คุณไม่ต้องการให้ CompareTo () และเท่ากับ () มีความสอดคล้องกันเนื่องจากเศษส่วนที่ลดลงซึ่งกันและกันไม่จำเป็นต้องเท่ากัน


1

วิธีทำความสะอาดที่ฉันชอบคือการกลับมาเพียงครั้งเดียว

 public int compareTo(Fraction frac) {
        int result = 0
        double t = this.doubleValue();
        double f = frac.doubleValue();
        if(t>f) 
           result = 1;
        else if(f>t) 
           result -1;
        return result;
    }


1

ฉันทำความสะอาดคำตอบของ cletus :

  • เพิ่ม Javadoc สำหรับวิธีการทั้งหมด
  • เพิ่มการตรวจสอบเงื่อนไขเบื้องต้นของวิธีการ
  • แทนที่การแยกวิเคราะห์ที่กำหนดเองvalueOf(String)ด้วยBigInteger(String)ซึ่งทั้งยืดหยุ่นและเร็วกว่า
import com.google.common.base.Splitter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;
import org.bitbucket.cowwoc.preconditions.Preconditions;

/**
 * A rational fraction, represented by {@code numerator / denominator}.
 * <p>
 * This implementation is based on <a
 * href="https://stackoverflow.com/a/474577/14731">https://stackoverflow.com/a/474577/14731</a>
 * <p>
 * @author Gili Tzabari
 */
public final class BigRational extends Number implements Comparable<BigRational>
{
    private static final long serialVersionUID = 0L;
    public static final BigRational ZERO = new BigRational(BigInteger.ZERO, BigInteger.ONE);
    public static final BigRational ONE = new BigRational(BigInteger.ONE, BigInteger.ONE);

    /**
     * Ensures the fraction the denominator is positive and optionally divides the numerator and
     * denominator by the greatest common factor.
     * <p>
     * @param numerator   a numerator
     * @param denominator a denominator
     * @param checkGcd    true if the numerator and denominator should be divided by the greatest
     *                    common factor
     * @return the canonical representation of the rational fraction
     */
    private static BigRational canonical(BigInteger numerator, BigInteger denominator,
        boolean checkGcd)
    {
        assert (numerator != null);
        assert (denominator != null);
        if (denominator.signum() == 0)
            throw new IllegalArgumentException("denominator is zero");
        if (numerator.signum() == 0)
            return ZERO;
        BigInteger newNumerator = numerator;
        BigInteger newDenominator = denominator;
        if (newDenominator.signum() < 0)
        {
            newNumerator = newNumerator.negate();
            newDenominator = newDenominator.negate();
        }
        if (checkGcd)
        {
            BigInteger gcd = newNumerator.gcd(newDenominator);
            if (!gcd.equals(BigInteger.ONE))
            {
                newNumerator = newNumerator.divide(gcd);
                newDenominator = newDenominator.divide(gcd);
            }
        }
        return new BigRational(newNumerator, newDenominator);
    }

    /**
     * @param numerator   a numerator
     * @param denominator a denominator
     * @return a BigRational having value {@code numerator / denominator}
     * @throws NullPointerException if numerator or denominator are null
     */
    public static BigRational valueOf(BigInteger numerator, BigInteger denominator)
    {
        Preconditions.requireThat(numerator, "numerator").isNotNull();
        Preconditions.requireThat(denominator, "denominator").isNotNull();
        return canonical(numerator, denominator, true);
    }

    /**
     * @param numerator   a numerator
     * @param denominator a denominator
     * @return a BigRational having value {@code numerator / denominator}
     */
    public static BigRational valueOf(long numerator, long denominator)
    {
        BigInteger bigNumerator = BigInteger.valueOf(numerator);
        BigInteger bigDenominator = BigInteger.valueOf(denominator);
        return canonical(bigNumerator, bigDenominator, true);
    }

    /**
     * @param value the parameter value
     * @param name  the parameter name
     * @return the BigInteger representation of the parameter
     * @throws NumberFormatException if value is not a valid representation of BigInteger
     */
    private static BigInteger requireBigInteger(String value, String name)
        throws NumberFormatException
    {
        try
        {
            return new BigInteger(value);
        }
        catch (NumberFormatException e)
        {
            throw (NumberFormatException) new NumberFormatException("Invalid " + name + ": " + value).
                initCause(e);
        }
    }

    /**
     * @param numerator   a numerator
     * @param denominator a denominator
     * @return a BigRational having value {@code numerator / denominator}
     * @throws NullPointerException     if numerator or denominator are null
     * @throws IllegalArgumentException if numerator or denominator are empty
     * @throws NumberFormatException    if numerator or denominator are not a valid representation of
     *                                  BigDecimal
     */
    public static BigRational valueOf(String numerator, String denominator)
        throws NullPointerException, IllegalArgumentException, NumberFormatException
    {
        Preconditions.requireThat(numerator, "numerator").isNotNull().isNotEmpty();
        Preconditions.requireThat(denominator, "denominator").isNotNull().isNotEmpty();
        BigInteger bigNumerator = requireBigInteger(numerator, "numerator");
        BigInteger bigDenominator = requireBigInteger(denominator, "denominator");
        return canonical(bigNumerator, bigDenominator, true);
    }

    /**
     * @param value a string representation of a rational fraction (e.g. "12.34e5" or "3/4")
     * @return a BigRational representation of the String
     * @throws NullPointerException     if value is null
     * @throws IllegalArgumentException if value is empty
     * @throws NumberFormatException    if numerator or denominator are not a valid representation of
     *                                  BigDecimal
     */
    public static BigRational valueOf(String value)
        throws NullPointerException, IllegalArgumentException, NumberFormatException
    {
        Preconditions.requireThat(value, "value").isNotNull().isNotEmpty();
        List<String> fractionParts = Splitter.on('/').splitToList(value);
        if (fractionParts.size() == 1)
            return valueOfRational(value);
        if (fractionParts.size() == 2)
            return BigRational.valueOf(fractionParts.get(0), fractionParts.get(1));
        throw new IllegalArgumentException("Too many slashes: " + value);
    }

    /**
     * @param value a string representation of a rational fraction (e.g. "12.34e5")
     * @return a BigRational representation of the String
     * @throws NullPointerException     if value is null
     * @throws IllegalArgumentException if value is empty
     * @throws NumberFormatException    if numerator or denominator are not a valid representation of
     *                                  BigDecimal
     */
    private static BigRational valueOfRational(String value)
        throws NullPointerException, IllegalArgumentException, NumberFormatException
    {
        Preconditions.requireThat(value, "value").isNotNull().isNotEmpty();
        BigDecimal bigDecimal = new BigDecimal(value);
        int scale = bigDecimal.scale();
        BigInteger numerator = bigDecimal.unscaledValue();
        BigInteger denominator;
        if (scale > 0)
            denominator = BigInteger.TEN.pow(scale);
        else
        {
            numerator = numerator.multiply(BigInteger.TEN.pow(-scale));
            denominator = BigInteger.ONE;
        }

        return canonical(numerator, denominator, true);
    }

    private final BigInteger numerator;
    private final BigInteger denominator;

    /**
     * @param numerator   the numerator
     * @param denominator the denominator
     * @throws NullPointerException if numerator or denominator are null
     */
    private BigRational(BigInteger numerator, BigInteger denominator)
    {
        Preconditions.requireThat(numerator, "numerator").isNotNull();
        Preconditions.requireThat(denominator, "denominator").isNotNull();
        this.numerator = numerator;
        this.denominator = denominator;
    }

    /**
     * @return the numerator
     */
    public BigInteger getNumerator()
    {
        return numerator;
    }

    /**
     * @return the denominator
     */
    public BigInteger getDenominator()
    {
        return denominator;
    }

    @Override
    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
    public int compareTo(BigRational other)
    {
        Preconditions.requireThat(other, "other").isNotNull();

        // canonical() ensures denominator is positive
        if (numerator.signum() != other.numerator.signum())
            return numerator.signum() - other.numerator.signum();

        // Set the denominator to a common multiple before comparing the numerators
        BigInteger first = numerator.multiply(other.denominator);
        BigInteger second = other.numerator.multiply(denominator);
        return first.compareTo(second);
    }

    /**
     * @param other another rational fraction
     * @return the result of adding this object to {@code other}
     * @throws NullPointerException if other is null
     */
    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
    public BigRational add(BigRational other)
    {
        Preconditions.requireThat(other, "other").isNotNull();
        if (other.numerator.signum() == 0)
            return this;
        if (numerator.signum() == 0)
            return other;
        if (denominator.equals(other.denominator))
            return new BigRational(numerator.add(other.numerator), denominator);
        return canonical(numerator.multiply(other.denominator).
            add(other.numerator.multiply(denominator)),
            denominator.multiply(other.denominator), true);
    }

    /**
     * @param other another rational fraction
     * @return the result of subtracting {@code other} from this object
     * @throws NullPointerException if other is null
     */
    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
    public BigRational subtract(BigRational other)
    {
        return add(other.negate());
    }

    /**
     * @param other another rational fraction
     * @return the result of multiplying this object by {@code other}
     * @throws NullPointerException if other is null
     */
    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
    public BigRational multiply(BigRational other)
    {
        Preconditions.requireThat(other, "other").isNotNull();
        if (numerator.signum() == 0 || other.numerator.signum() == 0)
            return ZERO;
        if (numerator.equals(other.denominator))
            return canonical(other.numerator, denominator, true);
        if (other.numerator.equals(denominator))
            return canonical(numerator, other.denominator, true);
        if (numerator.negate().equals(other.denominator))
            return canonical(other.numerator.negate(), denominator, true);
        if (other.numerator.negate().equals(denominator))
            return canonical(numerator.negate(), other.denominator, true);
        return canonical(numerator.multiply(other.numerator), denominator.multiply(other.denominator),
            true);
    }

    /**
     * @param other another rational fraction
     * @return the result of dividing this object by {@code other}
     * @throws NullPointerException if other is null
     */
    public BigRational divide(BigRational other)
    {
        return multiply(other.invert());
    }

    /**
     * @return true if the object is a whole number
     */
    public boolean isInteger()
    {
        return numerator.signum() == 0 || denominator.equals(BigInteger.ONE);
    }

    /**
     * Returns a BigRational whose value is (-this).
     * <p>
     * @return -this
     */
    public BigRational negate()
    {
        return new BigRational(numerator.negate(), denominator);
    }

    /**
     * @return a rational fraction with the numerator and denominator swapped
     */
    public BigRational invert()
    {
        return canonical(denominator, numerator, false);
    }

    /**
     * @return the absolute value of this {@code BigRational}
     */
    public BigRational abs()
    {
        if (numerator.signum() < 0)
            return negate();
        return this;
    }

    /**
     * @param exponent exponent to which both numerator and denominator is to be raised.
     * @return a BigRational whose value is (this<sup>exponent</sup>).
     */
    public BigRational pow(int exponent)
    {
        return canonical(numerator.pow(exponent), denominator.pow(exponent), true);
    }

    /**
     * @param other another rational fraction
     * @return the minimum of this object and the other fraction
     */
    public BigRational min(BigRational other)
    {
        if (compareTo(other) <= 0)
            return this;
        return other;
    }

    /**
     * @param other another rational fraction
     * @return the maximum of this object and the other fraction
     */
    public BigRational max(BigRational other)
    {
        if (compareTo(other) >= 0)
            return this;
        return other;
    }

    /**
     * @param scale        scale of the BigDecimal quotient to be returned
     * @param roundingMode the rounding mode to apply
     * @return a BigDecimal representation of this object
     * @throws NullPointerException if roundingMode is null
     */
    public BigDecimal toBigDecimal(int scale, RoundingMode roundingMode)
    {
        Preconditions.requireThat(roundingMode, "roundingMode").isNotNull();
        if (isInteger())
            return new BigDecimal(numerator);
        return new BigDecimal(numerator).divide(new BigDecimal(denominator), scale, roundingMode);
    }

    @Override
    public int intValue()
    {
        return (int) longValue();
    }

    @Override
    public long longValue()
    {
        if (isInteger())
            return numerator.longValue();
        return numerator.divide(denominator).longValue();
    }

    @Override
    public float floatValue()
    {
        return (float) doubleValue();
    }

    @Override
    public double doubleValue()
    {
        if (isInteger())
            return numerator.doubleValue();
        return numerator.doubleValue() / denominator.doubleValue();
    }

    @Override
    @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject")
    public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (!(o instanceof BigRational))
            return false;
        BigRational other = (BigRational) o;

        return numerator.equals(other.denominator) && Objects.equals(denominator, other.denominator);
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(numerator, denominator);
    }

    /**
     * Returns the String representation: {@code numerator / denominator}.
     */
    @Override
    public String toString()
    {
        if (isInteger())
            return String.format("%,d", numerator);
        return String.format("%,d / %,d", numerator, denominator);
    }
}

0

ข้อสังเกตเบื้องต้น:

อย่าเขียนสิ่งนี้:

if ( condition ) statement;

แบบนี้ดีกว่ามาก

if ( condition ) { statement };

เพียงสร้างเพื่อสร้างนิสัยที่ดี

ด้วยการทำให้คลาสไม่เปลี่ยนรูปตามที่แนะนำคุณยังสามารถใช้ประโยชน์จาก double เพื่อดำเนินการเท่ากับและ hashCode และเปรียบเทียบ

นี่คือเวอร์ชันสกปรกฉบับย่อของฉัน:

public final class Fraction implements Comparable {

    private final int numerator;
    private final int denominator;
    private final Double internal;

    public static Fraction createFraction( int numerator, int denominator ) { 
        return new Fraction( numerator, denominator );
    }

    private Fraction(int numerator, int denominator) {
        this.numerator   = numerator;
        this.denominator = denominator;
        this.internal = ((double) numerator)/((double) denominator);
    }


    public int getNumerator() {
        return this.numerator;
    }

    public int getDenominator() {
        return this.denominator;
    }


    private double doubleValue() {
        return internal;
    }

    public int compareTo( Object o ) {
        if ( o instanceof Fraction ) { 
            return internal.compareTo( ((Fraction)o).internal );
        }
        return 1;
    }

    public boolean equals( Object o ) {
          if ( o instanceof Fraction ) {  
             return this.internal.equals( ((Fraction)o).internal );
          } 
          return false;
    }

    public int hashCode() { 
        return internal.hashCode();
    }



    public String toString() { 
        return String.format("%d/%d", numerator, denominator );
    }

    public static void main( String [] args ) { 
        System.out.println( Fraction.createFraction( 1 , 2 ) ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).hashCode() ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).compareTo( Fraction.createFraction(2,4) ) ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).equals( Fraction.createFraction(4,8) ) ) ;
        System.out.println( Fraction.createFraction( 3 , 9 ).equals( Fraction.createFraction(1,3) ) ) ;
    }       

}

เกี่ยวกับวิธีการโรงงานแบบคงที่อาจเป็นประโยชน์ในภายหลังหากคุณย่อยคลาส Fraction เพื่อจัดการกับสิ่งที่ซับซ้อนมากขึ้นหรือหากคุณตัดสินใจใช้พูลสำหรับวัตถุที่ใช้บ่อยที่สุด

อาจไม่เป็นเช่นนั้นฉันแค่อยากจะชี้ให้เห็น :)

ดูรายการแรกของJava ที่มีประสิทธิภาพ


0

อาจเป็นประโยชน์ในการเพิ่มสิ่งง่ายๆเช่นการตอบสนองรับส่วนที่เหลือและรับทั้งหมด


คำตอบนี้เหมาะสำหรับแสดงความคิดเห็น
Jasonw

ขออภัยอย่างมากสำหรับการตอบกลับล่าช้า แต่ฉันเชื่อว่ามีจำนวนตัวแทนขั้นต่ำ (50?) ที่จำเป็นในการแสดงความคิดเห็นในคำตอบที่ฉันไม่มี ...
Darth Joshua

0

แม้ว่าคุณจะมีวิธีการ CompareTo () แต่หากคุณต้องการใช้ประโยชน์จากยูทิลิตี้เช่น Collections.sort () คุณควรใช้ Comparable ด้วย

public class Fraction extends Number implements Comparable<Fraction> {
 ...
}

นอกจากนี้สำหรับการแสดงผลที่สวยงามฉันขอแนะนำให้แทนที่ toString ()

public String toString() {
    return this.getNumerator() + "/" + this.getDenominator();
}

และสุดท้ายฉันจะทำให้ชั้นเรียนเป็นแบบสาธารณะเพื่อที่คุณจะได้ใช้จากแพ็คเกจต่างๆ


0

ฟังก์ชั่นนี้ทำให้ง่ายขึ้นโดยใช้อัลกอริทึมยูเซอลีเดียนมีประโยชน์มากเมื่อกำหนดเศษส่วน

 public Fraction simplify(){


     int safe;
     int h= Math.max(numerator, denominator);
     int h2 = Math.min(denominator, numerator);

     if (h == 0){

         return new Fraction(1,1);
     }

     while (h>h2 && h2>0){

          h = h - h2;
          if (h>h2){

              safe = h;
              h = h2;
              h2 = safe;

          }  

     }

  return new Fraction(numerator/h,denominator/h);

 }

0

สำหรับการใช้งาน Fraction / Rational ระดับอุตสาหกรรมฉันจะใช้มันเพื่อให้สามารถแสดงถึง NaN ค่าอนันต์บวกอินฟินิตี้เชิงลบและศูนย์ลบทางเลือกที่มีความหมายเชิงปฏิบัติการเหมือนกับสถานะมาตรฐาน IEEE 754 สำหรับเลขคณิตลอยตัว (นอกจากนี้ยังช่วยลดค่า การแปลงเป็น / จากค่าทศนิยม) นอกจากนี้เนื่องจากการเปรียบเทียบเป็นศูนย์หนึ่งและค่าพิเศษข้างต้นต้องการเพียงอย่างง่าย แต่การเปรียบเทียบตัวเศษและตัวส่วนร่วมกับ 0 และ 1 - ฉันจะเพิ่มวิธี isXXX และ CompareToXXX หลายวิธีเพื่อความสะดวกในการใช้งาน (เช่น eq0 () จะ ใช้ตัวเศษ == 0 && ตัวส่วน! = 0 อยู่เบื้องหลังแทนที่จะปล่อยให้ไคลเอนต์เปรียบเทียบกับอินสแตนซ์ที่มีค่าเป็นศูนย์) ค่าที่กำหนดไว้ล่วงหน้าแบบคงที่ (ZERO, ONE, TWO, TEN, ONE_TENTH, NAN ฯลฯ ) ก็มีประโยชน์เช่นกัน เนื่องจากปรากฏในหลายที่เป็นค่าคงที่ นี่เป็นวิธีที่ดีที่สุดของ IMHO


0

เศษส่วนของคลาส:

     public class Fraction {
        private int num;            // numerator 
        private int denom;          // denominator 
        // default constructor
        public Fraction() {}
        // constructor
        public Fraction( int a, int b ) {
            num = a;
            if ( b == 0 )
                throw new ZeroDenomException();
            else
                denom = b;
        }
        // return string representation of ComplexNumber
        @Override
        public String toString() {
            return "( " + num + " / " + denom + " )";
        }
        // the addition operation
        public Fraction add(Fraction x){
            return new Fraction(
                    x.num * denom + x.denom * num, x.denom * denom );
        }
        // the multiplication operation
        public Fraction multiply(Fraction x) {
            return new Fraction(x.num * num, x.denom * denom);
        } 
}

โปรแกรมหลัก:

    static void main(String[] args){
    Scanner input = new Scanner(System.in);
    System.out.println("Enter numerator and denominator of first fraction");
    int num1 =input.nextInt();
    int denom1 =input.nextInt();
    Fraction x = new Fraction(num1, denom1);
    System.out.println("Enter numerator and denominator of second fraction");
    int num2 =input.nextInt();
    int denom2 =input.nextInt();
    Fraction y = new Fraction(num2, denom2);
    Fraction result = new Fraction();
    System.out.println("Enter required operation: A (Add), M (Multiply)");
    char op = input.next().charAt(0);
    if(op == 'A') {
        result = x.add(y);
        System.out.println(x + " + " + y + " = " + result);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.