Revision: 4327 http://vexi.svn.sourceforge.net/vexi/?rev=4327&view=rev Author: mkpg2 Date: 2012-01-16 14:41:37 +0000 (Mon, 16 Jan 2012) Log Message: ----------- New class, Rational. Unlimited precision rational values.
Added Paths: ----------- trunk/org.vexi-library.value/src/main/java/org/vexi/value/Rational.java trunk/org.vexi-library.value/src/poke/ trunk/org.vexi-library.value/src/poke/java/ trunk/org.vexi-library.value/src/poke/java/poke/ trunk/org.vexi-library.value/src/poke/java/poke/PokeDivide.java trunk/org.vexi-library.value/src/poke/java/poke/PokeDoubleBits.java trunk/org.vexi-library.value/src/test/java/org/vexi/value/TestRational.java Property Changed: ---------------- trunk/org.vexi-library.value/ Property changes on: trunk/org.vexi-library.value ___________________________________________________________________ Modified: svn:ignore - .configuration gen .classpath .project .settings + .configuration gen .classpath .project .settings build release Added: trunk/org.vexi-library.value/src/main/java/org/vexi/value/Rational.java =================================================================== --- trunk/org.vexi-library.value/src/main/java/org/vexi/value/Rational.java (rev 0) +++ trunk/org.vexi-library.value/src/main/java/org/vexi/value/Rational.java 2012-01-16 14:41:37 UTC (rev 4327) @@ -0,0 +1,286 @@ +package org.vexi.value; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Rational extends Number implements Comparable { + static final public Rational INFINITY = new Rational(BigInteger.ONE, BigInteger.ZERO); + static final public Rational INFINITY_NEG = new Rational(BigInteger.valueOf(-1), BigInteger.ZERO); + static final public Rational NAN = new Rational(BigInteger.ZERO, BigInteger.ZERO); + static final public Rational ZERO = new Rational(BigInteger.ZERO, BigInteger.ONE); + static final public Rational ONE = new Rational(BigInteger.ONE, BigInteger.ONE); + + static private Rational canonical(BigInteger numerator, BigInteger denominator, boolean checkGcd) { + if (denominator.signum() == 0) { + if (numerator.signum() == 0) { + return NAN; + }else if(numerator.signum() >0){ + return INFINITY; + }else{ + return INFINITY_NEG; + } + } + 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 Rational(numerator, denominator); + } + + static public Rational valueOf(long n) { + return canonical(BigInteger.valueOf(n), BigInteger.ONE, false); + } + + static public Rational valueOf(BigInteger n) { + return canonical(n, BigInteger.ONE, false); + } + + + static public Rational valueOf(long numerator, long denominator) { + return canonical(new BigInteger("" + numerator), new BigInteger("" + denominator), true); + } + + static public Rational valueOf(BigInteger numerator, BigInteger denominator) { + return canonical(numerator, denominator, true); + } + + static public Rational valueOf(BigDecimal n) { + return valueOf(n.unscaledValue(), BigInteger.TEN.pow(n.scale())); + } + + static final private BigInteger BI_TWO = BigInteger.valueOf(2); + static public Rational valueOf(double d) { + if(0.0d==d){ + return ZERO; + }else if(Double.isNaN(d)){ + return NAN; + }else if(Double.isInfinite(d)) { + if(d>0) return INFINITY; + else return INFINITY_NEG; + } else { + long bits = Double.doubleToLongBits(d); + + int s = (((bits >> 63) == 0) ? 1 : -1); + int e = (int)((bits >> 52) & 0x7ffL); + long m = ((e == 0) ? + ((bits & 0xfffffffffffffL) << 1) + : ((bits & 0xfffffffffffffL) | 0x10000000000000L)); + + e -= 1075; + m = m * s; + + BigInteger num = BigInteger.valueOf(m); + BigInteger den = ((0 == e)? + BigInteger.ONE + : BI_TWO.shiftLeft(Math.abs(e)-1)); + + if (0 <= e) { + return valueOf(num.multiply(den), BigInteger.ONE); + } else { + return valueOf(num, den); + } + } + } + + static Pattern p = Pattern.compile("(-?\\d+)(?:.(\\d+)?)?0*(?:e(-?\\d+))?"); + static public Rational tryParse(String s) { + Matcher m = p.matcher(s); + if (!m.matches()) { + return null; + } + + // 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.parseInt(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); + } + + + + + final private BigInteger numerator, denominator; + + static int count; + private Rational(BigInteger numerator, BigInteger denominator) { + this.numerator = numerator; + this.denominator = denominator; + } + + // Comparable + public int compareTo(Object 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 + Rational r = (Rational)o; + if (numerator.signum() != r.numerator.signum()) { + return numerator.signum() - r.numerator.signum(); + } else { + // oddly BigInteger has gcd() but no lcm() + BigInteger i1 = numerator.multiply(r.denominator); + BigInteger i2 = r.numerator.multiply(denominator); + return i1.compareTo(i2); // expensive! + } + } + + public Rational add(Rational o) { + if (isNaN() || o.isNaN()) return NAN; + if (o.numerator.signum() == 0) { + return this; + } else if (numerator.signum() == 0) { + return o; + } else if (denominator.equals(o.denominator)) { + return Rational.valueOf(numerator.add(o.numerator), denominator); + } else { + return canonical(numerator.multiply(o.denominator).add(o.numerator.multiply(denominator)), denominator.multiply(o.denominator), true); + } + } + + + public Rational multiply(Rational o) { +// if (isNaN() || o.isNaN()) return NAN; + if (numerator.signum() == 0 || o.numerator.signum( )== 0) { + if(denominator.signum()==0 || o.denominator.signum()==0) + return NAN; + 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 isInt32() { return isInteger() && getNumerator().bitLength()<32; } + public boolean isInteger() { return numerator.signum() == 0 || denominator.equals(BigInteger.ONE); } + public boolean isNaN() { return numerator.signum()==0 && denominator.signum()==0; } + public boolean isFinite() { return denominator.signum()!=0; } + + public Rational negate() { return new Rational(numerator.negate(), denominator); } + public Rational invert() { return canonical(denominator, numerator, false); } + public Rational abs() { return numerator.signum() < 0 ? negate() : this; } + public Rational pow(int exp) { return canonical(numerator.pow(exp), denominator.pow(exp), true); } + public Rational subtract(Rational o) { return add(o.negate()); } + public Rational divide(Rational o) { + return multiply(o.invert()); + } + + public Rational mod(Rational o){ + BigInteger left = numerator.multiply(o.denominator); + BigInteger right = o.numerator.multiply(denominator); + BigInteger r = left.mod(right); + if(numerator.signum()<0 && !BigInteger.ZERO.equals(left)){ + r = right.abs().subtract(r); + } + return valueOf(r, denominator.multiply(o.denominator)); + } + public Rational min(Rational o) { return compareTo(o) <= 0 ? this : o; } + public Rational max(Rational o) { return compareTo(o) >= 0 ? this : o; } + + public Rational ceil() { + BigInteger[] ii = numerator.divideAndRemainder(denominator); + BigInteger result = ii[0]; + BigInteger remainder = ii[1]; + if(remainder.signum()==0) return valueOf(result); + return valueOf(result.add(BigInteger.ONE)); + } + public Rational floor() { + BigInteger[] ii = numerator.divideAndRemainder(denominator); + BigInteger result = ii[0]; + BigInteger remainder = ii[1]; + if(remainder.signum()==0) return valueOf(result); + return valueOf(result.subtract(BigInteger.ONE)); + } + public Rational round() { + BigInteger[] ii = numerator.divideAndRemainder(denominator); + BigInteger result = ii[0]; + BigInteger remainder = ii[1]; + if(remainder.multiply(BI_TWO).compareTo(denominator)>=0){ + result.add(BigInteger.ONE); + } + return valueOf(result); + } + + 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(); } + + public String toString() { + if(!isFinite()){ + if(numerator.signum()==0) return "NaN"; + else if(numerator.signum()>0) return "Infinity"; + else return "-Infinity"; + } + return isInteger() ? numerator+"" : numerator+" / "+denominator; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Rational that = (Rational) 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; + } + + public int hashCode() { + int result = numerator != null ? numerator.hashCode() : 0; + result = 31 * result + (denominator != null ? denominator.hashCode() : 0); + return result; + } + + + + +} + Property changes on: trunk/org.vexi-library.value/src/main/java/org/vexi/value/Rational.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: trunk/org.vexi-library.value/src/poke/java/poke/PokeDivide.java =================================================================== --- trunk/org.vexi-library.value/src/poke/java/poke/PokeDivide.java (rev 0) +++ trunk/org.vexi-library.value/src/poke/java/poke/PokeDivide.java 2012-01-16 14:41:37 UTC (rev 4327) @@ -0,0 +1,10 @@ +package poke; + +public class PokeDivide { + static public void main(String[] args) { + System.err.println(0.123456d%0.01); +// System.err.println(5/4); +// System.err.println(Math.floor(-5d/4)); + + } +} Property changes on: trunk/org.vexi-library.value/src/poke/java/poke/PokeDivide.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: trunk/org.vexi-library.value/src/poke/java/poke/PokeDoubleBits.java =================================================================== --- trunk/org.vexi-library.value/src/poke/java/poke/PokeDoubleBits.java (rev 0) +++ trunk/org.vexi-library.value/src/poke/java/poke/PokeDoubleBits.java 2012-01-16 14:41:37 UTC (rev 4327) @@ -0,0 +1,59 @@ +package poke; + +import java.math.BigDecimal; + +public class PokeDoubleBits { + + static class DoubleParts{ + boolean negative; + int exponent; + long mantissa; + public String toDecimalString() { + BigDecimal bd = new BigDecimal(mantissa).divide(BigDecimal.valueOf(0x0010000000000000L)); + return ((negative)?"-":"+")+bd+"*2^"+exponent; + } + public String toString() { + return ((negative)?"-":"+")+"1."+Long.toBinaryString((mantissa&0x000fffffffffffffL))+"*2^"+exponent; + } + } + + static public DoubleParts deconstructDouble(double d){ + + long bits = Double.doubleToLongBits(d); + long sign = bits & 0x8000000000000000L; + int exponent = (int)(((bits & 0x7ff0000000000000L)>>52)-1023); + + long mantissa = (bits & 0x000fffffffffffffL) | 0x0010000000000000L; // implicit bit + DoubleParts r = new DoubleParts(); + r.negative = sign>>63==1; + r.exponent = exponent; + r.mantissa = mantissa; + return r; + } + + static public double constructDouble(DoubleParts ps){ return constructDouble(ps.negative, ps.mantissa, ps.exponent); } + static public double constructDouble(boolean negative, long mantissa, int exponent){ + if(exponent>=0x7ff) throw new Error("Expronent too large: "+mantissa); + if(mantissa>0x001fffffffffffffL) throw new Error("Mantissa too large: "+mantissa); + long bits = ((long)exponent+1023)<<52 | (mantissa & 0x000fffffffffffffL); + if(negative) bits |=0x7000000000000000L; + return Double.longBitsToDouble(bits); + } + + static public void assertDouble(double d){ + DoubleParts ps = deconstructDouble(d); + double d2 = constructDouble(ps); + System.err.println(d); + System.err.println(ps.toString()); + System.err.println(d2); + if(d!=d2) throw new Error(d+"!="+d2); + } + + + static public void main(String[] args) { + assertDouble(2.25); + assertDouble(4.125); + } + +} + Property changes on: trunk/org.vexi-library.value/src/poke/java/poke/PokeDoubleBits.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: trunk/org.vexi-library.value/src/test/java/org/vexi/value/TestRational.java =================================================================== --- trunk/org.vexi-library.value/src/test/java/org/vexi/value/TestRational.java (rev 0) +++ trunk/org.vexi-library.value/src/test/java/org/vexi/value/TestRational.java 2012-01-16 14:41:37 UTC (rev 4327) @@ -0,0 +1,21 @@ +package org.vexi.value; + +public class TestRational { + static public void dump(String name, Rational r) { + System.out.println(name+" "+r); + System.out.println(); + } + + static public void main(String args[]) { + Rational r1 = Rational.valueOf(31400); + Rational r2 = Rational.valueOf(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)); + } + +} Property changes on: trunk/org.vexi-library.value/src/test/java/org/vexi/value/TestRational.java ___________________________________________________________________ Added: svn:mime-type + text/plain This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ RSA(R) Conference 2012 Mar 27 - Feb 2 Save $400 by Jan. 27 Register now! http://p.sf.net/sfu/rsa-sfdev2dev2 _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn