| /* java.math.BigDecimal -- Arbitrary precision decimals. | |
| Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
| 02111-1307 USA. | |
| As a special exception, if you link this library with other files to | |
| produce an executable, this library does not by itself cause the | |
| resulting executable to be covered by the GNU General Public License. | |
| This exception does not however invalidate any other reasons why the | |
| executable file might be covered by the GNU General Public License. */ | |
| package java.math; | |
| import java.math.BigInteger; | |
| public class BigDecimal extends Number implements Comparable | |
| { | |
| private BigInteger intVal; | |
| private int scale; | |
| private static final long serialVersionUID = 6108874887143696463L; | |
| private final static BigDecimal ZERO = | |
| new BigDecimal (BigInteger.valueOf (0), 0); | |
| private final static BigDecimal ONE = | |
| new BigDecimal (BigInteger.valueOf (1), 0); | |
| public final static int ROUND_UP = 0; | |
| public final static int ROUND_DOWN = 1; | |
| public final static int ROUND_CEILING = 2; | |
| public final static int ROUND_FLOOR = 3; | |
| public final static int ROUND_HALF_UP = 4; | |
| public final static int ROUND_HALF_DOWN = 5; | |
| public final static int ROUND_HALF_EVEN = 6; | |
| public final static int ROUND_UNNECESSARY = 7; | |
| public BigDecimal (BigInteger num) | |
| { | |
| this (num, 0); | |
| } | |
| public BigDecimal (BigInteger num, int scale) throws NumberFormatException | |
| { | |
| if (scale < 0) | |
| throw new NumberFormatException ("scale of " + scale + " is < 0"); | |
| this.intVal = num; | |
| this.scale = scale; | |
| } | |
| public BigDecimal (double num) throws NumberFormatException | |
| { | |
| if (Double.isInfinite (num) || Double.isNaN (num)) | |
| throw new NumberFormatException ("invalid argument: " + num); | |
| // Note we can't convert NUM to a String and then use the | |
| // String-based constructor. The BigDecimal documentation makes | |
| // it clear that the two constructors work differently. | |
| final int mantissaBits = 52; | |
| final int exponentBits = 11; | |
| final long mantMask = (1L << mantissaBits) - 1; | |
| final long expMask = (1L << exponentBits) - 1; | |
| long bits = Double.doubleToLongBits (num); | |
| long mantissa = bits & mantMask; | |
| long exponent = (bits >>> mantissaBits) & expMask; | |
| boolean denormal = exponent == 0; | |
| // Correct the exponent for the bias. | |
| exponent -= denormal ? 1022 : 1023; | |
| // Now correct the exponent to account for the bits to the right | |
| // of the decimal. | |
| exponent -= mantissaBits; | |
| // Ordinary numbers have an implied leading `1' bit. | |
| if (! denormal) | |
| mantissa |= (1L << mantissaBits); | |
| // Shave off factors of 10. | |
| while (exponent < 0 && (mantissa & 1) == 0) | |
| { | |
| ++exponent; | |
| mantissa >>= 1; | |
| } | |
| intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa); | |
| if (exponent < 0) | |
| { | |
| // We have MANTISSA * 2 ^ (EXPONENT). | |
| // Since (1/2)^N == 5^N * 10^-N we can easily convert this | |
| // into a power of 10. | |
| scale = (int) (- exponent); | |
| BigInteger mult = BigInteger.valueOf (5).pow (scale); | |
| intVal = intVal.multiply (mult); | |
| } | |
| else | |
| { | |
| intVal = intVal.shiftLeft ((int) exponent); | |
| scale = 0; | |
| } | |
| } | |
| public BigDecimal (String num) throws NumberFormatException | |
| { | |
| int len = num.length(); | |
| int start = 0, point = 0; | |
| int dot = -1; | |
| boolean negative = false; | |
| if (num.charAt(0) == '+') | |
| { | |
| ++start; | |
| ++point; | |
| } | |
| else if (num.charAt(0) == '-') | |
| { | |
| ++start; | |
| ++point; | |
| negative = true; | |
| } | |
| while (point < len) | |
| { | |
| char c = num.charAt (point); | |
| if (c == '.') | |
| { | |
| if (dot >= 0) | |
| throw new NumberFormatException ("multiple `.'s in number"); | |
| dot = point; | |
| } | |
| else if (c == 'e' || c == 'E') | |
| break; | |
| else if (Character.digit (c, 10) < 0) | |
| throw new NumberFormatException ("unrecognized character: " + c); | |
| ++point; | |
| } | |
| String val; | |
| if (dot >= 0) | |
| { | |
| val = num.substring (start, dot) + num.substring (dot + 1, point); | |
| scale = point - 1 - dot; | |
| } | |
| else | |
| { | |
| val = num.substring (start, point); | |
| scale = 0; | |
| } | |
| if (val.length () == 0) | |
| throw new NumberFormatException ("no digits seen"); | |
| if (negative) | |
| val = "-" + val; | |
| intVal = new BigInteger (val); | |
| // Now parse exponent. | |
| if (point < len) | |
| { | |
| int exp = Integer.parseInt (num.substring (point + 1)); | |
| exp -= scale; | |
| if (exp > 0) | |
| { | |
| intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp)); | |
| scale = 0; | |
| } | |
| else | |
| scale = - exp; | |
| } | |
| } | |
| public static BigDecimal valueOf (long val) | |
| { | |
| return valueOf (val, 0); | |
| } | |
| public static BigDecimal valueOf (long val, int scale) | |
| throws NumberFormatException | |
| { | |
| if (scale == 0) | |
| switch ((int) val) | |
| { | |
| case 0: | |
| return ZERO; | |
| case 1: | |
| return ONE; | |
| } | |
| return new BigDecimal (BigInteger.valueOf (val), scale); | |
| } | |
| public BigDecimal add (BigDecimal val) | |
| { | |
| // For addition, need to line up decimals. Note that the movePointRight | |
| // method cannot be used for this as it might return a BigDecimal with | |
| // scale == 0 instead of the scale we need. | |
| BigInteger op1 = intVal; | |
| BigInteger op2 = val.intVal; | |
| if (scale < val.scale) | |
| op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale)); | |
| else if (scale > val.scale) | |
| op2 = op2.multiply (BigInteger.valueOf (10).pow (scale - val.scale)); | |
| return new BigDecimal (op1.add (op2), Math.max (scale, val.scale)); | |
| } | |
| public BigDecimal subtract (BigDecimal val) | |
| { | |
| return this.add(val.negate()); | |
| } | |
| public BigDecimal multiply (BigDecimal val) | |
| { | |
| return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); | |
| } | |
| public BigDecimal divide (BigDecimal val, int roundingMode) | |
| throws ArithmeticException, IllegalArgumentException | |
| { | |
| return divide (val, scale, roundingMode); | |
| } | |
| public BigDecimal divide(BigDecimal val, int newScale, int roundingMode) | |
| throws ArithmeticException, IllegalArgumentException | |
| { | |
| if (roundingMode < 0 || roundingMode > 7) | |
| throw | |
| new IllegalArgumentException("illegal rounding mode: " + roundingMode); | |
| if (newScale < 0) | |
| throw new ArithmeticException ("scale is negative: " + newScale); | |
| if (intVal.signum () == 0) // handle special case of 0.0/0.0 | |
| return ZERO; | |
| // Ensure that pow gets a non-negative value. | |
| int valScale = val.scale; | |
| BigInteger valIntVal = val.intVal; | |
| int power = newScale + 1 - (scale - val.scale); | |
| if (power < 0) | |
| { | |
| // Effectively increase the scale of val to avoid an | |
| // ArithmeticException for a negative power. | |
| valIntVal = valIntVal.multiply (BigInteger.valueOf (10).pow (-power)); | |
| power = 0; | |
| } | |
| BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (power)); | |
| BigInteger parts[] = dividend.divideAndRemainder (valIntVal); | |
| // System.out.println("int: " + parts[0]); | |
| // System.out.println("rem: " + parts[1]); | |
| int roundDigit = parts[0].mod (BigInteger.valueOf (10)).intValue (); | |
| BigInteger unrounded = parts[0].divide (BigInteger.valueOf (10)); | |
| if (roundDigit == 0 && parts[1].signum () == 0) // no rounding necessary | |
| return new BigDecimal (unrounded, newScale); | |
| int sign = unrounded.signum (); | |
| switch (roundingMode) | |
| { | |
| case ROUND_UNNECESSARY: | |
| throw new ArithmeticException ("newScale is not large enough"); | |
| case ROUND_CEILING: | |
| roundingMode = (sign == 1) ? ROUND_UP : ROUND_DOWN; | |
| break; | |
| case ROUND_FLOOR: | |
| roundingMode = (sign == 1) ? ROUND_DOWN : ROUND_UP; | |
| break; | |
| case ROUND_HALF_UP: | |
| roundingMode = (roundDigit >= 5) ? ROUND_UP : ROUND_DOWN; | |
| break; | |
| case ROUND_HALF_DOWN: | |
| roundingMode = (roundDigit > 5) ? ROUND_UP : ROUND_DOWN; | |
| break; | |
| case ROUND_HALF_EVEN: | |
| if (roundDigit < 5) | |
| roundingMode = ROUND_DOWN; | |
| else | |
| { | |
| int rightmost = | |
| unrounded.mod (BigInteger.valueOf (10)).intValue (); | |
| if (rightmost % 2 == 1) // odd, then ROUND_HALF_UP | |
| roundingMode = ROUND_UP; | |
| else // even, then ROUND_HALF_DOWN | |
| roundingMode = (roundDigit > 5) ? ROUND_UP : ROUND_DOWN; | |
| } | |
| break; | |
| } | |
| if (roundingMode == ROUND_UP) | |
| return new BigDecimal (unrounded.add (BigInteger.valueOf (1)), newScale); | |
| // roundingMode == ROUND_DOWN | |
| return new BigDecimal (unrounded, newScale); | |
| } | |
| public int compareTo (BigDecimal val) | |
| { | |
| if (scale == val.scale) | |
| return intVal.compareTo (val.intVal); | |
| BigInteger thisParts[] = | |
| intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); | |
| BigInteger valParts[] = | |
| val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); | |
| int compare; | |
| if ((compare = thisParts[0].compareTo (valParts[0])) != 0) | |
| return compare; | |
| // quotients are the same, so compare remainders | |
| // remove trailing zeros | |
| if (thisParts[1].equals (BigInteger.valueOf (0)) == false) | |
| while (thisParts[1].mod (BigInteger.valueOf (10)).equals | |
| (BigInteger.valueOf (0))) | |
| thisParts[1] = thisParts[1].divide (BigInteger.valueOf (10)); | |
| // again... | |
| if (valParts[1].equals(BigInteger.valueOf (0)) == false) | |
| while (valParts[1].mod (BigInteger.valueOf (10)).equals | |
| (BigInteger.valueOf (0))) | |
| valParts[1] = valParts[1].divide (BigInteger.valueOf (10)); | |
| // and compare them | |
| return thisParts[1].compareTo (valParts[1]); | |
| } | |
| public int compareTo (Object val) | |
| { | |
| return(compareTo((BigDecimal)val)); | |
| } | |
| public boolean equals (Object o) | |
| { | |
| return (o instanceof BigDecimal | |
| && scale == ((BigDecimal) o).scale | |
| && compareTo ((BigDecimal) o) == 0); | |
| } | |
| public int hashCode() | |
| { | |
| return intValue() ^ scale; | |
| } | |
| public BigDecimal max (BigDecimal val) | |
| { | |
| switch (compareTo (val)) | |
| { | |
| case 1: | |
| return this; | |
| default: | |
| return val; | |
| } | |
| } | |
| public BigDecimal min (BigDecimal val) | |
| { | |
| switch (compareTo (val)) | |
| { | |
| case -1: | |
| return this; | |
| default: | |
| return val; | |
| } | |
| } | |
| public BigDecimal movePointLeft (int n) | |
| { | |
| return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n); | |
| } | |
| public BigDecimal movePointRight (int n) | |
| { | |
| if (n < 0) | |
| return movePointLeft (-n); | |
| if (scale >= n) | |
| return new BigDecimal (intVal, scale - n); | |
| return new BigDecimal (intVal.multiply | |
| (BigInteger.valueOf (10).pow (n - scale)), 0); | |
| } | |
| public int signum () | |
| { | |
| return intVal.signum (); | |
| } | |
| public int scale () | |
| { | |
| return scale; | |
| } | |
| public BigDecimal abs () | |
| { | |
| return new BigDecimal (intVal.abs (), scale); | |
| } | |
| public BigDecimal negate () | |
| { | |
| return new BigDecimal (intVal.negate (), scale); | |
| } | |
| public String toString () | |
| { | |
| String bigStr = intVal.toString(); | |
| if (scale == 0) | |
| return bigStr; | |
| int point = bigStr.length() - scale; | |
| boolean negative = (bigStr.charAt(0) == '-'); | |
| StringBuffer sb = new StringBuffer(bigStr.length() + 1 + | |
| (point <= 0 ? -point+1 : 0)); | |
| if (negative) | |
| sb.append('-'); | |
| while (point <= 0) | |
| { | |
| sb.append('0'); | |
| point++; | |
| } | |
| sb.append(bigStr.substring(negative ? 1 : 0)); | |
| sb.insert(point, '.'); | |
| return sb.toString(); | |
| } | |
| public BigInteger toBigInteger () | |
| { | |
| return scale == 0 ? intVal : | |
| intVal.divide (BigInteger.valueOf (10).pow (scale)); | |
| } | |
| public int intValue () | |
| { | |
| return toBigInteger ().intValue (); | |
| } | |
| public long longValue () | |
| { | |
| return toBigInteger().longValue(); | |
| } | |
| public float floatValue() | |
| { | |
| return Float.valueOf(toString()).floatValue(); | |
| } | |
| public double doubleValue() | |
| { | |
| return Double.valueOf(toString()).doubleValue(); | |
| } | |
| public BigDecimal setScale (int scale) throws ArithmeticException | |
| { | |
| return setScale (scale, ROUND_UNNECESSARY); | |
| } | |
| public BigDecimal setScale (int scale, int roundingMode) | |
| throws ArithmeticException, IllegalArgumentException | |
| { | |
| return divide (ONE, scale, roundingMode); | |
| } | |
| } |