【问题标题】:Best way to represent a fraction in Java?用Java表示分数的最佳方法?
【发布时间】:2010-10-03 05:42:16
【问题描述】:

我正在尝试在 Java 中使用 fractions

我想实现算术函数。为此,我首先需要一种方法来规范化函数。我知道在我有一个共同点之前,我不能添加 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;
    }
}

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

【问题讨论】:

  • 扔掉代码,使用 Apache Commons :) commons.apache.org/math/userguide/fraction.html
  • Patrick 的评论值得 +1,如果它被发布为答案。在大多数情况下,这是正确的答案;正如 Effective Java 所说,“了解并使用库”。原来的问题也很清楚也很有用。
  • 注意到您接受了我的回答。如果您确实在使用该代码并发现它有任何问题或缺少任何东西,请告诉我!从我的网站给我发电子邮件:vacant-nebula.com/contact/kip
  • 我建议您编辑您的“compareTo”方法,并将“this.getNumerator()”转换为长 before 乘法。否则代码仍然容易溢出。另外我认为实现 Comparable 会很好,因为您已经实现了 compareTo 方法。
  • 既然你已经走了这么远,实现equals和hashCode也可能很有用。

标签: java math fractions


【解决方案1】:

好吧,首先,我会摆脱 setter 并让 Fractions 不可变。

您可能还需要加法、减法等方法,也许还需要某种方法来获取各种字符串格式的表示。

编辑:我可能会将这些字段标记为“最终”以表明我的意图,但我想这没什么大不了的......

【讨论】:

  • 我想知道我们最终会得到多少“使其不可变”的答案:)
【解决方案2】:

一个非常小的改进可能是保存您正在计算的双精度值,以便您只在第一次访问时计算它。除非您经常访问此号码,否则这不会是一个很大的胜利,但也不是很难做到。

另外一点可能是您在分母中进行的错误检查...您会自动将 0 更改为 1。不确定这对于您的特定应用程序是否正确,但通常如果有人试图除以 0,则是非常错误的。我会让它抛出一个异常(如果你觉得有必要,这是一个专门的异常),而不是以用户不知道的看似任意的方式更改值。

与其他一些 cmets 相比,关于添加方法以添加减法等...由于您没有提到需要它们,我假设您不需要它们。除非你正在构建一个真正会在许多地方或被其他人使用的库,否则请使用 YAGNI(你不会需要它,所以它不应该在那里。)

【讨论】:

  • 他拥有 getNumerator() 和 getDenominator() 的事实让我相信他在这个班级之外创造了新的分数。如果存在,该逻辑可能属于这里。
  • +1 默默地将分母中的 0 更改为 1 会导致灾难。
【解决方案3】:

请使其成为不可变类型!分数的值不会改变——例如,一半不会变成三分之一。您可以使用 withDenominator 代替 setDenominator,它返回一个 new 分数,该分数具有相同的分子但指定的分母。

使用不可变类型让生活轻松得多

覆盖 equals 和 hashcode 也是明智的,因此它可以用于地图和集合中。 Outlaw Programmer 关于算术运算符和字符串格式化的观点也很好。

作为一般指南,请查看 BigInteger 和 BigDecimal。他们做的事情不一样,但他们足够相似,可以给你很好的想法。

【讨论】:

  • "请使其成为不可变类型!分数的值不会改变 - 例如,一半不会变成三分之一。"列表/元组/向量 (1, 2, 3, 4) 也不会变成值 (4, 3, 2, 1),但它似乎并不困扰大多数列表更改状态的人。并不是说我不同意分数的不变性,但它值得一个更好的论据。感觉更像是一个价值,而不是一堆状态。程序员的期望是正确的理由吗?我不是 100% 确定,但这听起来是个好主意。
  • 嗯,在现实生活中的清单改变:你如何写一个购物清单?你从一张白纸开始,然后在上面写字。进行到一半时,您仍将其称为“购物清单”。话虽如此,函数式编程确实努力使列表不可变......
【解决方案4】:

创建分数对象后,为什么要允许其他对象设置分子或分母?我认为这些应该是只读的。它使对象不可变...

另外...将分母设置为零应该会抛出一个无效的参数异常(我不知道它在 Java 中是什么)

【讨论】:

  • 或者 throw new ArithmeticException("除以零。")
【解决方案5】:

我需要按从小到大的顺序排列它们, 所以最终我也需要将它们表示为双重

不是绝对必要的。 (事实上​​,如果你想正确处理相等,不要依赖 double 来正常工作。)如果 b*d 是正数,如果 ad

我可能会改写为:

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

(p.s. 你可能会考虑重组为你的班级实现 ComparableComparator。)

【讨论】:

  • 如果 a = 1, b = 3, c = -2, d = -3 则不成立。如果 b 和 d 为正,则当且仅当 ad 时 a/b
  • 啊,我弄错了资格。 (谢谢!)条件应该是如果 bd > 0。
  • 是的。更准确地说,如果 bd > 0,a/b ac
  • 关闭。你的意思是 a/b ad 0。 (我在我的代码 cmets 中第一次就做对了!)
【解决方案6】:

我将如何改进该代码:

  1. 一个基于String Fraction(String s)的构造函数 //expect "number/number"
  2. 复制构造函数Fraction(Fraction copy)
  3. 覆盖克隆方法
  4. 实现 equals、toString 和 hashcode 方法
  5. 实现接口 java.io.Serializable, Comparable
  6. 方法“double getDoubleValue()”
  7. 方法加/除/等...
  8. 我会将该类设置为不可变的(没有设置器)

【讨论】:

  • 一个不错的列表。可能不需要克隆/可序列化,但其他一切都是合理的。
  • @OutlawProgrammer:是的,8 或 3。可克隆的不可变是无意义的。
【解决方案7】:

事实上,试穿它的大小。它可以运行,但可能存在一些问题:

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

【讨论】:

    【解决方案8】:

    有几种方法可以改进这个或任何值类型:

    • 使您的班级immutable,包括使分子和分母最终化
    • 自动convert fractions to a canonical form,例如2/4 -> 1/2
    • 实现 toString()
    • 实现“public static Fraction valueOf(String s)”以将字符串转换为分数。实现类似的工厂方法从 int、double 等转换。
    • 实现加法、乘法等
    • 从整数添加构造函数
    • 覆盖等于/hashCode
    • 考虑将 Fraction 设为接口,并在必要时切换到 BigInteger 的实现
    • 考虑子分类Number
    • 考虑为常用值(如 0 和 1)包含命名常量
    • 考虑使其可序列化
    • 测试除以零
    • 记录您的 API

    基本上,看看其他值类的 API,如 Double、Integer 并做他们所做的事情:)

    【讨论】:

      【解决方案9】:
      • 如果没有 add() 和 multiply() 等算术方法,这有点毫无意义。
      • 您绝对应该重写 equals() 和 hashCode()。
      • 您应该添加一种方法来标准化分数,或者自动执行。考虑一下您是否希望将 1/2 和 2/4 视为相同 - 这对 equals()、hashCode() 和 compareTo() 方法有影响。

      【讨论】:

        【解决方案10】:

        如果您将一个分数的分子和分母与另一个分数的分母相乘,反之亦然,您最终会得到两个分母相同的分数(仍然是相同的值),您可以直接比较分子。因此,您不需要计算双精度值:

        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 有相反的符号,这将失败。 (请参阅我的帖子。)您还必须注意乘法可能溢出的事实。
        • 啊,是的,没错。但在那种情况下,我更喜欢 Kip 的实现,我至少可以理解。 ;)
        • 我要指出,在我的实现中,只有分子可以是负数。我也使用 BigIntegers,因此永远不会发生溢出(当然,以牺牲一些性能为代价)。
        【解决方案11】:

        恰好我不久前为Project Euler problems 编写了一个BigFraction 类。它保留一个 BigInteger 分子和分母,因此它永远不会溢出。但是对于很多你知道永远不会溢出的操作来说,它会有点慢。无论如何,如果你想要它,就使用它。我一直渴望以某种方式炫耀这一点。 :)

        编辑:此代码的最新和最好的版本,包括单元测试是now hosted on GitHubavailable via 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;
            }
          }
        
        }
        

        【讨论】:

        • 如果 arg 为 null,则抛出 NullPointerException。事实上,代码无论如何都会这样做,所以你的检查(和替换为 IllegalArgumentException( 是不必要的代码膨胀。
        • 我不同意;如果另一个用户在没有查看我的源代码的情况下使用这个类,并得到一个 NullPointerException,他会认为 my 代码中存在错误。但是 IllegalArgumentException 表明他违反了 javadoc 所暗示的合同(即使我没有明确说明)。
        • 只是一个问题,Commons Math 中的 Fraction 和 BigFraction 有什么问题?
        • @Mortimer:不确定,我从没看过
        【解决方案12】:

        Timothy Budd 在他的“C++ 中的数据结构”中很好地实现了 Rational 类。当然是不同的语言,但它可以很好地移植到 Java。

        我会推荐更多的构造函数。默认构造函数的分子为 0,分母为 1。单个 arg 构造函数的分母为 1。想想你的用户可能如何使用这个类。

        不检查零分母?按合同编程会让你添加它。

        【讨论】:

          【解决方案13】:

          初始备注:

          永远不要这样写:

          if ( condition ) statement;
          

          这样更好

          if ( condition ) { statement };
          

          创造只是为了养成一个好习惯。

          通过按照建议使类不可变,您还可以利用 double 来执行 equals 和 hashCode 以及 compareTo 操作

          这是我的快速脏版本:

          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 子类化以处理更复杂的事情,或者如果您决定将池用于最常用的对象,它可能会很有用。

          可能不是这样,我只是想指出这一点。 :)

          参见Effective Java 第一项。

          【讨论】:

            【解决方案14】:

            我将第三或第五或任何使您的分数不可变的建议。我还建议你让它扩展 Number 类。我可能会查看Double 类,因为您可能想要实现许多相同的方法。

            您可能还应该实现ComparableSerializable,因为这种行为可能是意料之中的。因此,您将需要实现 compareTo()。你还需要重写equals(),我不能强调你也重写hashCode()。这可能是您不希望 compareTo() 和 equals() 保持一致的少数情况之一,因为可归约的分数不一定相等。

            【讨论】:

              【解决方案15】:

              你已经有一个 compareTo 函数......我会实现 Comparable 接口。

              不过,无论你打算用它做什么,都可能并不重要。

              【讨论】:

                【解决方案16】:

                如果您喜欢冒险,请查看 JScience。它有一个代表分数的Rational 类。

                【讨论】:

                  【解决方案17】:

                  我喜欢的一种清理做法是只有一次回报。

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

                  【讨论】:

                    【解决方案18】:

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

                    Apache Commons Math 开设Fraction 课程已经有一段时间了。大多数时候的答案是,“男孩,我希望 Java 在核心库中有类似 X 的东西!”可以在Apache Commons library 的保护伞下找到。

                    【讨论】:

                    • 我会告诉你为什么这么低,Apache Commons 库对新手不友好。首先,该页面上没有直接下载链接(它隐藏在侧边栏菜单中),其次没有关于如何使用它的说明(将 jar 添加到构建路径),第三,无论如何添加后我得到了一个 classDefNotFound 错误.所以你不会得到我们这些只知道如何复制和粘贴的人的支持。
                    • @Noumenon 如何使用任何构建管理器(例如 maven)并在 POM 中添加依赖项?
                    • 我想看看一些“如何在你的项目中使用它”的小白。这个建议可以进去。也就是说,我确实想出了如何做到这一点,并在我的工厂应用程序中使用了它,该应用程序需要显示英寸的分数,我再也没有回来给你投票。所以谢谢,来晚了。
                    • 这是公平的反馈。这也是我迟来的感谢! :)
                    • 这个非常好用。
                    【解决方案19】:

                    特别是:有没有更好的方法来处理通过零分母?将分母设置为 1 感觉非常随意。我怎样才能做到这一点?

                    我会说抛出一个除以零的 ArithmeticException,因为这确实发生了:

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

                    您可能想让消息说“除以零:分数的分母为零”,而不是“除以零。”

                    【讨论】:

                      【解决方案20】:

                      使用来自JScience 库的 Rational 类。这是我在 Java 中看到的最适合小数运算的东西。

                      【讨论】:

                        【解决方案21】:

                        可能有助于添加简单的事情,例如互惠、获得余数和获得整体。

                        【讨论】:

                        • 这个答案适合作为评论。
                        • 对于迟到的回复非常抱歉,但我相信对我没有的答案发表评论需要最少的代表(50?)...
                        【解决方案22】:

                        即使你有 compareTo() 方法,如果你想使用 Collections.sort() 之类的实用程序,那么你也应该实现 Comparable。

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

                        另外,为了漂亮的显示,我建议覆盖 toString()

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

                        最后,我会将这个类公开,以便您可以从不同的包中使用它。

                        【讨论】:

                          【解决方案23】:

                          我清理了cletus' answer

                          • 为所有方法添加了 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);
                              }
                          }
                          

                          【讨论】:

                            【解决方案24】:

                            这个函数使用欧几里得算法简化在定义分数时非常有用

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

                            【讨论】:

                              【解决方案25】:

                              对于工业级分数/有理实现,我将实现它,以便它可以表示 NaN、正无穷大、负无穷大和可选的负零,其操作语义与浮点算术的 IEEE 754 标准状态完全相同(它还简化了与浮点值之间的转换)。另外,由于与零、一和上面的特殊值的比较只需要简单,但分子和分母与 0 和 1 的组合比较 - 我会添加几个 isXXX 和 compareToXXX 方法以方便使用(例如 eq0() 将在幕后使用 numerator == 0 && denominator != 0 而不是让客户端与零值实例进行比较)。一些静态预定义的值(零、一、二、十、一_十、南等)也很有用,因为它们作为常量值出现在几个地方。恕我直言,这是最好的方法。

                              【讨论】:

                                【解决方案26】:

                                类分数:

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

                                【讨论】:

                                  猜你喜欢
                                  相关资源
                                  最近更新 更多
                                  热门标签