我正在尝试使用Java中的分数.
我想实现算术函数.为此,我首先要求一种方法来规范化功能.我知道我不能加1/6和1/2,直到我有一个共同点.我将不得不添加1/6和3/6.一个天真的方法会让我添加2/12和6/12,然后减少.如何实现性能损失最小的共同点?什么算法最适合这个?
版本8(感谢hstoerr):
改进包括:
equals()方法现在与compareTo()方法一致
final class Fraction extends Number { private int numerator; private int denominator; public Fraction(int numerator, int denominator) { if(denominator == 0) { throw new IllegalArgumentException("denominator is zero"); } if(denominator < 0) { numerator *= -1; denominator *= -1; } this.numerator = numerator; this.denominator = denominator; } public Fraction(int numerator) { this.numerator = numerator; this.denominator = 1; } public int getNumerator() { return this.numerator; } public int getDenominator() { return this.denominator; } public byte byteValue() { return (byte) this.doubleValue(); } public double doubleValue() { return ((double) numerator)/((double) denominator); } public float floatValue() { return (float) this.doubleValue(); } public int intValue() { return (int) this.doubleValue(); } public long longValue() { return (long) this.doubleValue(); } public short shortValue() { return (short) this.doubleValue(); } public boolean equals(Fraction frac) { return this.compareTo(frac) == 0; } public int compareTo(Fraction frac) { long t = this.getNumerator() * frac.getDenominator(); long f = frac.getNumerator() * this.getDenominator(); int result = 0; if(t>f) { result = 1; } else if(f>t) { result = -1; } return result; } }
我已删除所有以前的版本.谢谢你:
戴夫雷
克莱图斯
duffymo
詹姆士
米尔豪斯
奥斯卡雷耶斯
杰森S.
Francisco Canedo
歹徒程序员
Beska
Kip.. 61
碰巧我不久前写了一个BigFraction类,用于Project Euler问题.它保留了一个BigInteger分子和分母,因此它永远不会溢出.但是对于你知道永远不会溢出的许多操作来说,这将是一个缓慢的过程..无论如何,如果你想要它,请使用它.我一直渴望以某种方式表明这一点.:)
编辑:此代码的最新版本和最佳版本,包括单元测试现在托管在GitHub上,也可以通过Maven Central获得.我将原始代码保留在此处,以便此答案不仅仅是一个链接......
import java.math.*; /** * Arbitrary-precision fractions, utilizing BigIntegers for numerator and * denominator. Fraction is always kept in lowest terms. Fraction is * immutable, and guaranteed not to have a null numerator or denominator. * Denominator will always be positive (so sign is carried by numerator, * and a zero-denominator is impossible). */ public final class BigFraction extends Number implements Comparable{ 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 * BigDecimal(String)
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,他会认为*my*代码中存在错误.但是IllegalArgumentException表明他违反了javadoc隐含的合同(即使我没有明确说明). (23认同)
http://stackoverflow.com/questions/3881/illegalargumentexception-or-nullpointerexception-for-a-null-parameter#8160 (2认同)
cletus.. 58
让它一成不变 ;
使其成为规范,意味着6/4变为3/2(最大公约数算法对此有用);
称之为理性,因为你所代表的是一个有理数 ;
您可以使用BigInteger
存储任意精确的值.如果不是那么long
,那么实施起来更容易;
使分母始终为正.标志应由分子携带;
延伸Number
;
实施Comparable
;
实施equals()
和hashCode()
;
为a表示的数字添加工厂方法String
;
添加一些方便的工厂方法;
添加一个toString()
; 和
做吧Serializable
.
事实上,尝试这个尺寸.它运行但可能有一些问题:
public class BigRational extends Number implements Comparable, 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
yawmark.. 28
我正在尝试使用Java中的适当分数.
Apache Commons Math在很长一段时间内都有一个Fraction类.大多数时候,答案是"男孩,我希望Java 在核心库中有类似X的东西!" 可以在Apache Commons库的保护下找到.
碰巧我不久前写了一个BigFraction类,用于Project Euler问题.它保留了一个BigInteger分子和分母,因此它永远不会溢出.但是对于你知道永远不会溢出的许多操作来说,这将是一个缓慢的过程..无论如何,如果你想要它,请使用它.我一直渴望以某种方式表明这一点.:)
编辑:此代码的最新版本和最佳版本,包括单元测试现在托管在GitHub上,也可以通过Maven Central获得.我将原始代码保留在此处,以便此答案不仅仅是一个链接......
import java.math.*; /** * Arbitrary-precision fractions, utilizing BigIntegers for numerator and * denominator. Fraction is always kept in lowest terms. Fraction is * immutable, and guaranteed not to have a null numerator or denominator. * Denominator will always be positive (so sign is carried by numerator, * and a zero-denominator is impossible). */ public final class BigFraction extends Number implements Comparable{ 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 * BigDecimal(String)
constructor. * * @throws NumberFormatException if the string cannot be properly parsed. */ public BigFraction(String s) { int slashPos = s.indexOf('/'); if(slashPos < 0) { BigFraction res = new BigFraction(new BigDecimal(s)); this.numerator = res.numerator; this.denominator = res.denominator; } else { BigDecimal num = new BigDecimal(s.substring(0, slashPos)); BigDecimal den = new BigDecimal(s.substring(slashPos+1, s.length())); BigFraction res = new BigFraction(num, den); this.numerator = res.numerator; this.denominator = res.denominator; } } /** * Returns this + f. */ public BigFraction add(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); //n1/d1 + n2/d2 = (n1*d2 + d1*n2)/(d1*d2) return new BigFraction(numerator.multiply(f.denominator).add(denominator.multiply(f.numerator)), denominator.multiply(f.denominator)); } /** * Returns this + b. */ public BigFraction add(BigInteger b) { if(b == null) throw new IllegalArgumentException("Null argument"); //n1/d1 + n2 = (n1 + d1*n2)/d1 return new BigFraction(numerator.add(denominator.multiply(b)), denominator, true); } /** * Returns this + n. */ public BigFraction add(long n) { return add(BigInteger.valueOf(n)); } /** * Returns this - f. */ public BigFraction subtract(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); return new BigFraction(numerator.multiply(f.denominator).subtract(denominator.multiply(f.numerator)), denominator.multiply(f.denominator)); } /** * Returns this - b. */ public BigFraction subtract(BigInteger b) { if(b == null) throw new IllegalArgumentException("Null argument"); return new BigFraction(numerator.subtract(denominator.multiply(b)), denominator, true); } /** * Returns this - n. */ public BigFraction subtract(long n) { return subtract(BigInteger.valueOf(n)); } /** * Returns this * f. */ public BigFraction multiply(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); return new BigFraction(numerator.multiply(f.numerator), denominator.multiply(f.denominator)); } /** * Returns this * b. */ public BigFraction multiply(BigInteger b) { if(b == null) throw new IllegalArgumentException("Null argument"); return new BigFraction(numerator.multiply(b), denominator); } /** * Returns this * n. */ public BigFraction multiply(long n) { return multiply(BigInteger.valueOf(n)); } /** * Returns this / f. */ public BigFraction divide(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); if(f.numerator.equals(BigInteger.ZERO)) throw new ArithmeticException("Divide by zero"); return new BigFraction(numerator.multiply(f.denominator), denominator.multiply(f.numerator)); } /** * Returns this / b. */ public BigFraction divide(BigInteger b) { if(b == null) throw new IllegalArgumentException("Null argument"); if(b.equals(BigInteger.ZERO)) throw new ArithmeticException("Divide by zero"); return new BigFraction(numerator, denominator.multiply(b)); } /** * Returns this / n. */ public BigFraction divide(long n) { return divide(BigInteger.valueOf(n)); } /** * Returns this^exponent. */ public BigFraction pow(int exponent) { if(exponent == 0) return BigFraction.ONE; else if (exponent == 1) return this; else if (exponent < 0) return new BigFraction(denominator.pow(-exponent), numerator.pow(-exponent), true); else return new BigFraction(numerator.pow(exponent), denominator.pow(exponent), true); } /** * Returns 1/this. */ public BigFraction reciprocal() { if(this.numerator.equals(BigInteger.ZERO)) throw new ArithmeticException("Divide by zero"); return new BigFraction(denominator, numerator, true); } /** * Returns the complement of this fraction, which is equal to 1 - this. * Useful for probabilities/statistics. */ public BigFraction complement() { return new BigFraction(denominator.subtract(numerator), denominator, true); } /** * Returns -this. */ public BigFraction negate() { return new BigFraction(numerator.negate(), denominator, true); } /** * Returns -1, 0, or 1, representing the sign of this fraction. */ public int signum() { return numerator.signum(); } /** * Returns the absolute value of this. */ public BigFraction abs() { return (signum() < 0 ? negate() : this); } /** * Returns a string representation of this, in the form * numerator/denominator. */ public String toString() { return numerator.toString() + "/" + denominator.toString(); } /** * Returns if this object is equal to another object. */ public boolean equals(Object o) { if(!(o instanceof BigFraction)) return false; BigFraction f = (BigFraction)o; return numerator.equals(f.numerator) && denominator.equals(f.denominator); } /** * Returns a hash code for this object. */ public int hashCode() { //using the method generated by Eclipse, but streamlined a bit.. return (31 + numerator.hashCode())*31 + denominator.hashCode(); } /** * Returns a negative, zero, or positive number, indicating if this object * is less than, equal to, or greater than f, respectively. */ public int compareTo(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); //easy case: this and f have different signs if(signum() != f.signum()) return signum() - f.signum(); //next easy case: this and f have the same denominator if(denominator.equals(f.denominator)) return numerator.compareTo(f.numerator); //not an easy case, so first make the denominators equal then compare the numerators return numerator.multiply(f.denominator).compareTo(denominator.multiply(f.numerator)); } /** * Returns the smaller of this and f. */ public BigFraction min(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); return (this.compareTo(f) <= 0 ? this : f); } /** * Returns the maximum of this and f. */ public BigFraction max(BigFraction f) { if(f == null) throw new IllegalArgumentException("Null argument"); return (this.compareTo(f) >= 0 ? this : f); } /** * Returns a positive BigFraction, greater than or equal to zero, and less than one. */ public static BigFraction random() { return new BigFraction(Math.random()); } public final BigInteger getNumerator() { return numerator; } public final BigInteger getDenominator() { return denominator; } //implementation of Number class. may cause overflow. public byte byteValue() { return (byte) Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, longValue())); } public short shortValue() { return (short)Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, longValue())); } public int intValue() { return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, longValue())); } public long longValue() { return Math.round(doubleValue()); } public float floatValue() { return (float)doubleValue(); } public double doubleValue() { return toBigDecimal(18).doubleValue(); } /** * Returns a BigDecimal representation of this fraction. If possible, the * returned value will be exactly equal to the fraction. If not, the BigDecimal * will have a scale large enough to hold the same number of significant figures * as both numerator and denominator, or the equivalent of a double-precision * number, whichever is more. */ public BigDecimal toBigDecimal() { //Implementation note: A fraction can be represented exactly in base-10 iff its //denominator is of the form 2^a * 5^b, where a and b are nonnegative integers. //(In other words, if there are no prime factors of the denominator except for //2 and 5, or if the denominator is 1). So to determine if this denominator is //of this form, continually divide by 2 to get the number of 2's, and then //continually divide by 5 to get the number of 5's. Afterward, if the denominator //is 1 then there are no other prime factors. //Note: number of 2's is given by the number of trailing 0 bits in the number int twos = denominator.getLowestSetBit(); BigInteger tmpDen = denominator.shiftRight(twos); // x / 2^n === x >> n final BigInteger FIVE = BigInteger.valueOf(5); int fives = 0; BigInteger[] divMod = null; //while(tmpDen % 5 == 0) { fives++; tmpDen /= 5; } while(BigInteger.ZERO.equals((divMod = tmpDen.divideAndRemainder(FIVE))[1])) { fives++; tmpDen = divMod[0]; } if(BigInteger.ONE.equals(tmpDen)) { //This fraction will terminate in base 10, so it can be represented exactly as //a BigDecimal. We would now like to make the fraction of the form //unscaled / 10^scale. We know that 2^x * 5^x = 10^x, and our denominator is //in the form 2^twos * 5^fives. So use max(twos, fives) as the scale, and //multiply the numerator and deminator by the appropriate number of 2's or 5's //such that the denominator is of the form 2^scale * 5^scale. (Of course, we //only have to actually multiply the numerator, since all we need for the //BigDecimal constructor is the scale. BigInteger unscaled = numerator; int scale = Math.max(twos, fives); if(twos < fives) unscaled = unscaled.shiftLeft(fives - twos); //x * 2^n === x << n else if (fives < twos) unscaled = unscaled.multiply(FIVE.pow(twos - fives)); return new BigDecimal(unscaled, scale); } //else: this number will repeat infinitely in base-10. So try to figure out //a good number of significant digits. Start with the number of digits required //to represent the numerator and denominator in base-10, which is given by //bitLength / log[2](10). (bitLenth is the number of digits in base-2). final double LG10 = 3.321928094887362; //Precomputed ln(10)/ln(2), a.k.a. log[2](10) int precision = Math.max(numerator.bitLength(), denominator.bitLength()); precision = (int)Math.ceil(precision / LG10); //If the precision is less than 18 digits, use 18 digits so that the number //will be at least as accurate as a cast to a double. For example, with //the fraction 1/3, precision will be 1, giving a result of 0.3. This is //quite a bit different from what a user would expect. if(precision < 18) precision = 18; return toBigDecimal(precision); } /** * Returns a BigDecimal representation of this fraction, with a given precision. * @param precision the number of significant figures to be used in the result. */ public BigDecimal toBigDecimal(int precision) { return new BigDecimal(numerator).divide(new BigDecimal(denominator), new MathContext(precision, RoundingMode.HALF_EVEN)); } //-------------------------------------------------------------------------- // PRIVATE FUNCTIONS //-------------------------------------------------------------------------- /** * Private constructor, used when you can be certain that the fraction is already in * lowest terms. No check is done to reduce numerator/denominator. A check is still * done to maintain a positive denominator. * * @param throwaway unused variable, only here to signal to the compiler that this * constructor should be used. */ private BigFraction(BigInteger numerator, BigInteger denominator, boolean throwaway) { if(denominator.signum() < 0) { this.numerator = numerator.negate(); this.denominator = denominator.negate(); } else { this.numerator = numerator; this.denominator = denominator; } } }
让它一成不变 ;
使其成为规范,意味着6/4变为3/2(最大公约数算法对此有用);
称之为理性,因为你所代表的是一个有理数 ;
您可以使用BigInteger
存储任意精确的值.如果不是那么long
,那么实施起来更容易;
使分母始终为正.标志应由分子携带;
延伸Number
;
实施Comparable
;
实施equals()
和hashCode()
;
为a表示的数字添加工厂方法String
;
添加一些方便的工厂方法;
添加一个toString()
; 和
做吧Serializable
.
事实上,尝试这个尺寸.它运行但可能有一些问题:
public class BigRational extends Number implements Comparable, Serializable { public final static BigRational ZERO = new BigRational(BigInteger.ZERO, BigInteger.ONE); private final static long serialVersionUID = 1099377265582986378L; private final BigInteger numerator, denominator; private BigRational(BigInteger numerator, BigInteger denominator) { this.numerator = numerator; this.denominator = denominator; } private static BigRational canonical(BigInteger numerator, BigInteger denominator, boolean checkGcd) { if (denominator.signum() == 0) { throw new IllegalArgumentException("denominator is zero"); } if (numerator.signum() == 0) { return ZERO; } if (denominator.signum() < 0) { numerator = numerator.negate(); denominator = denominator.negate(); } if (checkGcd) { BigInteger gcd = numerator.gcd(denominator); if (!gcd.equals(BigInteger.ONE)) { numerator = numerator.divide(gcd); denominator = denominator.divide(gcd); } } return new BigRational(numerator, denominator); } public static BigRational getInstance(BigInteger numerator, BigInteger denominator) { return canonical(numerator, denominator, true); } public static BigRational getInstance(long numerator, long denominator) { return canonical(new BigInteger("" + numerator), new BigInteger("" + denominator), true); } public static BigRational getInstance(String numerator, String denominator) { return canonical(new BigInteger(numerator), new BigInteger(denominator), true); } public static BigRational valueOf(String s) { Pattern p = Pattern.compile("(-?\\d+)(?:.(\\d+)?)?0*(?:e(-?\\d+))?"); Matcher m = p.matcher(s); if (!m.matches()) { throw new IllegalArgumentException("Unknown format '" + s + "'"); } // this translates 23.123e5 to 25,123 / 1000 * 10^5 = 2,512,300 / 1 (GCD) String whole = m.group(1); String decimal = m.group(2); String exponent = m.group(3); String n = whole; // 23.123 => 23123 if (decimal != null) { n += decimal; } BigInteger numerator = new BigInteger(n); // exponent is an int because BigInteger.pow() takes an int argument // it gets more difficult if exponent needs to be outside {-2 billion,2 billion} int exp = exponent == null ? 0 : Integer.valueOf(exponent); int decimalPlaces = decimal == null ? 0 : decimal.length(); exp -= decimalPlaces; BigInteger denominator; if (exp < 0) { denominator = BigInteger.TEN.pow(-exp); } else { numerator = numerator.multiply(BigInteger.TEN.pow(exp)); denominator = BigInteger.ONE; } // done return canonical(numerator, denominator, true); } // Comparable public int compareTo(BigRational o) { // note: this is a bit of cheat, relying on BigInteger.compareTo() returning // -1, 0 or 1. For the more general contract of compareTo(), you'd need to do // more checking if (numerator.signum() != o.numerator.signum()) { return numerator.signum() - o.numerator.signum(); } else { // oddly BigInteger has gcd() but no lcm() BigInteger i1 = numerator.multiply(o.denominator); BigInteger i2 = o.numerator.multiply(denominator); return i1.compareTo(i2); // expensive! } } public BigRational add(BigRational o) { if (o.numerator.signum() == 0) { return this; } else if (numerator.signum() == 0) { return o; } else if (denominator.equals(o.denominator)) { return new BigRational(numerator.add(o.numerator), denominator); } else { return canonical(numerator.multiply(o.denominator).add(o.numerator.multiply(denominator)), denominator.multiply(o.denominator), true); } } public BigRational multiply(BigRational o) { if (numerator.signum() == 0 || o.numerator.signum( )== 0) { return ZERO; } else if (numerator.equals(o.denominator)) { return canonical(o.numerator, denominator, true); } else if (o.numerator.equals(denominator)) { return canonical(numerator, o.denominator, true); } else if (numerator.negate().equals(o.denominator)) { return canonical(o.numerator.negate(), denominator, true); } else if (o.numerator.negate().equals(denominator)) { return canonical(numerator.negate(), o.denominator, true); } else { return canonical(numerator.multiply(o.numerator), denominator.multiply(o.denominator), true); } } public BigInteger getNumerator() { return numerator; } public BigInteger getDenominator() { return denominator; } public boolean isInteger() { return numerator.signum() == 0 || denominator.equals(BigInteger.ONE); } public BigRational negate() { return new BigRational(numerator.negate(), denominator); } public BigRational invert() { return canonical(denominator, numerator, false); } public BigRational abs() { return numerator.signum() < 0 ? negate() : this; } public BigRational pow(int exp) { return canonical(numerator.pow(exp), denominator.pow(exp), true); } public BigRational subtract(BigRational o) { return add(o.negate()); } public BigRational divide(BigRational o) { return multiply(o.invert()); } public BigRational min(BigRational o) { return compareTo(o) <= 0 ? this : o; } public BigRational max(BigRational o) { return compareTo(o) >= 0 ? this : o; } public BigDecimal toBigDecimal(int scale, RoundingMode roundingMode) { return isInteger() ? new BigDecimal(numerator) : new BigDecimal(numerator).divide(new BigDecimal(denominator), scale, roundingMode); } // Number public int intValue() { return isInteger() ? numerator.intValue() : numerator.divide(denominator).intValue(); } public long longValue() { return isInteger() ? numerator.longValue() : numerator.divide(denominator).longValue(); } public float floatValue() { return (float)doubleValue(); } public double doubleValue() { return isInteger() ? numerator.doubleValue() : numerator.doubleValue() / denominator.doubleValue(); } @Override public String toString() { return isInteger() ? String.format("%,d", numerator) : String.format("%,d / %,d", numerator, denominator); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BigRational that = (BigRational) o; if (denominator != null ? !denominator.equals(that.denominator) : that.denominator != null) return false; if (numerator != null ? !numerator.equals(that.numerator) : that.numerator != null) return false; return true; } @Override public int hashCode() { int result = numerator != null ? numerator.hashCode() : 0; result = 31 * result + (denominator != null ? denominator.hashCode() : 0); return result; } public static void main(String args[]) { BigRational r1 = BigRational.valueOf("3.14e4"); BigRational r2 = BigRational.getInstance(111, 7); dump("r1", r1); dump("r2", r2); dump("r1 + r2", r1.add(r2)); dump("r1 - r2", r1.subtract(r2)); dump("r1 * r2", r1.multiply(r2)); dump("r1 / r2", r1.divide(r2)); dump("r2 ^ 2", r2.pow(2)); } public static void dump(String name, BigRational r) { System.out.printf("%s = %s%n", name, r); System.out.printf("%s.negate() = %s%n", name, r.negate()); System.out.printf("%s.invert() = %s%n", name, r.invert()); System.out.printf("%s.intValue() = %,d%n", name, r.intValue()); System.out.printf("%s.longValue() = %,d%n", name, r.longValue()); System.out.printf("%s.floatValue() = %,f%n", name, r.floatValue()); System.out.printf("%s.doubleValue() = %,f%n", name, r.doubleValue()); System.out.println(); } }
输出是:
r1 = 31,400 r1.negate() = -31,400 r1.invert() = 1 / 31,400 r1.intValue() = 31,400 r1.longValue() = 31,400 r1.floatValue() = 31,400.000000 r1.doubleValue() = 31,400.000000 r2 = 111 / 7 r2.negate() = -111 / 7 r2.invert() = 7 / 111 r2.intValue() = 15 r2.longValue() = 15 r2.floatValue() = 15.857142 r2.doubleValue() = 15.857143 r1 + r2 = 219,911 / 7 r1 + r2.negate() = -219,911 / 7 r1 + r2.invert() = 7 / 219,911 r1 + r2.intValue() = 31,415 r1 + r2.longValue() = 31,415 r1 + r2.floatValue() = 31,415.857422 r1 + r2.doubleValue() = 31,415.857143 r1 - r2 = 219,689 / 7 r1 - r2.negate() = -219,689 / 7 r1 - r2.invert() = 7 / 219,689 r1 - r2.intValue() = 31,384 r1 - r2.longValue() = 31,384 r1 - r2.floatValue() = 31,384.142578 r1 - r2.doubleValue() = 31,384.142857 r1 * r2 = 3,485,400 / 7 r1 * r2.negate() = -3,485,400 / 7 r1 * r2.invert() = 7 / 3,485,400 r1 * r2.intValue() = 497,914 r1 * r2.longValue() = 497,914 r1 * r2.floatValue() = 497,914.281250 r1 * r2.doubleValue() = 497,914.285714 r1 / r2 = 219,800 / 111 r1 / r2.negate() = -219,800 / 111 r1 / r2.invert() = 111 / 219,800 r1 / r2.intValue() = 1,980 r1 / r2.longValue() = 1,980 r1 / r2.floatValue() = 1,980.180176 r1 / r2.doubleValue() = 1,980.180180 r2 ^ 2 = 12,321 / 49 r2 ^ 2.negate() = -12,321 / 49 r2 ^ 2.invert() = 49 / 12,321 r2 ^ 2.intValue() = 251 r2 ^ 2.longValue() = 251 r2 ^ 2.floatValue() = 251.448975 r2 ^ 2.doubleValue() = 251.448980
我正在尝试使用Java中的适当分数.
Apache Commons Math在很长一段时间内都有一个Fraction类.大多数时候,答案是"男孩,我希望Java 在核心库中有类似X的东西!" 可以在Apache Commons库的保护下找到.
请使它成为不可变类型!例如,分数的值不会改变 - 一半不会变成第三个.您可以使用withDenominator而不是setDenominator,它返回一个具有相同分子但指定分母的新分数.
不可变类型的生活更容易.
覆盖equals和hashcode也是明智的,因此它可以用在地图和集合中.Outlaw Programmer关于算术运算符和字符串格式的要点也很好.
作为一般指南,请查看BigInteger和BigDecimal.他们没有做同样的事情,但他们的相似性足以给你很好的想法.
好吧,对于其中之一,我将摆脱制定者并使分数不可变.
你可能也想要添加,减去等方法,也许还有一些方法来获得各种String格式的表示.
编辑:我可能会将这些字段标记为"最终"以表示我的意图,但我想这不是什么大问题......
我需要从最小到最大排序它们,所以最终我还需要将它们表示为双精度
不是绝对必要的.(事实上,如果你想正确处理相等,不要依赖double来正常工作.)如果b*d是正数,a/b 我可能会改写为: 不确定性能与使用双打进行比较的效果如何.(也就是说,如果你关心性能那么多)这是我用来检查的测试方法.(似乎正常工作.) (ps你可能会考虑重组实施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
这里的用法是确保如果你乘以两个大的int
s 就没有溢出.handle如果你可以保证分母总是非负的(如果它是负数,只是否定分子和分母),那么你可以摆脱必须检查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
为你的班级.)
没有像add()和multiply()等算术方法,这有点无意义.
你绝对应该重写equals()和hashCode().
您应该添加一个方法来标准化分数,或者自动执行.想想你是否想要1/2和2/4被认为是相同的 - 这对equals(),hashCode()和compareTo()方法有影响.