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
[email protected]
https://lists.sourceforge.net/lists/listinfo/vexi-svn