home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 24.4 KB | 671 lines |
- /*
- * @(#)BigDecimal.java 1.12 98/03/18
- *
- * Copyright 1996, 1997 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.math;
-
- /**
- * Immutable, arbitrary-precision signed decimal numbers. A BigDecimal
- * consists of an arbitrary precision integer value and a non-negative
- * integer scale, which represents the number of decimal digits to the
- * right of the decimal point. (The number represented by the BigDecimal
- * is intVal/10**scale.) BigDecimals provide operations for basic arithmetic,
- * scale manipulation, comparison, format conversion and hashing.
- *
- * <p>The BigDecimal class gives its user complete control over rounding
- * behavior, forcing the user to explicitly specify a rounding behavior for
- * operations capable of discarding precision (divide and setScale). Eight
- * <em>rounding modes</em> are provided for this purpose.
- *
- * Two types of operations are provided for manipulating the scale of a
- * BigDecimal: scaling/rounding operations and decimal point motion operations.
- * Scaling/Rounding operations (SetScale) return a BigDecimal whose value is
- * approximately (or exactly) equal to that of the operand, but whose scale is
- * the specified value; that is, they increase or decrease the precision
- * of the number with minimal effect on its value. Decimal point motion
- * operations (movePointLeft and movePointRight) return a BigDecimal created
- * from the operand by moving the decimal point a specified distance in the
- * specified direction; that is, they change a number's value without affecting
- * its precision.
- *
- * @see BigInteger
- * @version 1.12, 98/03/18
- * @author Josh Bloch
- */
- public class BigDecimal extends Number implements Comparable {
- private BigInteger intVal;
- private int scale = 0;
-
- /* Appease the serialization gods */
- private static final long serialVersionUID = 6108874887143696463L;
-
- // Constructors
-
- /**
- * Constructs a BigDecimal from a string containing an optional minus
- * sign followed by a sequence of zero or more decimal digits, optionally
- * followed by a fraction, which consists of a decimal point followed by
- * zero or more decimal digits. The string must contain at least one
- * digit in the integer or fractional part. The scale of the resulting
- * BigDecimal will be the number of digits to the right of the decimal
- * point in the string, or 0 if the string contains no decimal point.
- * The character-to-digit mapping is provided by Character.digit.
- * Any extraneous characters (including whitespace) will result in
- * a NumberFormatException.
- */
- public BigDecimal(String val) throws NumberFormatException {
- int pointPos = val.indexOf('.');
- if (pointPos == -1) { /* e.g. "123" */
- intVal = new BigInteger(val);
- } else if (pointPos == val.length()-1) { /* e.g. "123." */
- intVal = new BigInteger(val.substring(0, val.length()-1));
- } else { /* Fraction part exists */
- String fracString = val.substring(pointPos+1);
- scale = fracString.length();
- BigInteger fraction = new BigInteger(fracString);
- if (fraction.signum() < 0) /* ".-123" illegal! */
- throw new NumberFormatException();
-
- if (pointPos==0) { /* e.g. ".123" */
- intVal = fraction;
- } else if (val.charAt(0)=='-' && pointPos==1) {
- intVal = fraction.negate(); /* e.g. "-.123" */
- } else { /* e.g. "-123.456" */
- String intString = val.substring(0, pointPos);
- BigInteger intPart = new BigInteger(intString);
- if (val.charAt(0) == '-')
- fraction = fraction.negate();
- intVal = timesTenToThe(intPart, scale).add(fraction);
- }
- }
- }
-
- /**
- * Translates a double into a BigDecimal. The scale of the BigDecimal
- * is the smallest value such that (10**scale * val) is an integer.
- * A double whose value is -infinity, +infinity or NaN will result in a
- * NumberFormatException.
- */
- public BigDecimal(double val) throws NumberFormatException{
- if (Double.isInfinite(val) || Double.isNaN(val))
- throw new NumberFormatException("Infinite or NaN");
-
- /*
- * Translate the double into sign, exponent and mantissa, according
- * to the formulae in JLS, Section 20.10.22.
- */
- long valBits = Double.doubleToLongBits(val);
- int sign = ((valBits >> 63)==0 ? 1 : -1);
- int exponent = (int) ((valBits >> 52) & 0x7ffL);
- long mantissa = (exponent==0 ? (valBits & ((1L<<52) - 1)) << 1
- : (valBits & ((1L<<52) - 1)) | (1L<<52));
- exponent -= 1075;
- /* At this point, val == sign * mantissa * 2**exponent */
-
- /*
- * Special case zero to to supress nonterminating normalization
- * and bogus scale calculation.
- */
- if (mantissa == 0) {
- intVal = BigInteger.valueOf(0);
- return;
- }
-
- /* Normalize */
- while((mantissa & 1) == 0) { /* i.e., Mantissa is even */
- mantissa >>= 1;
- exponent++;
- }
-
- /* Calculate intVal and scale */
- intVal = BigInteger.valueOf(sign*mantissa);
- if (exponent < 0) {
- intVal = intVal.multiply(BigInteger.valueOf(5).pow(-exponent));
- scale = -exponent;
- } else if (exponent > 0) {
- intVal = intVal.multiply(BigInteger.valueOf(2).pow(exponent));
- }
- }
-
- /**
- * Translates a BigInteger into a BigDecimal. The scale of the BigDecimal
- * is zero.
- */
- public BigDecimal(BigInteger val) {
- intVal = val;
- }
-
- /**
- * Translates a BigInteger and a scale into a BigDecimal. The value
- * of the BigDecimal is (BigInteger/10**scale). A negative scale
- * will result in a NumberFormatException.
- */
- public BigDecimal(BigInteger val, int scale) throws NumberFormatException {
- if (scale < 0)
- throw new NumberFormatException("Negative scale");
-
- intVal = val;
- this.scale = scale;
- }
-
-
- // Static Factory Methods
-
- /**
- * Returns a BigDecimal with a value of (val/10**scale). This factory
- * is provided in preference to a (long) constructor because it allows
- * for reuse of frequently used BigDecimals (like 0 and 1), obviating
- * the need for exported constants. A negative scale will result in a
- * NumberFormatException.
- */
- public static BigDecimal valueOf(long val, int scale)
- throws NumberFormatException {
- return new BigDecimal(BigInteger.valueOf(val), scale);
- }
-
- /**
- * Returns a BigDecimal with the given value and a scale of zero.
- * This factory is provided in preference to a (long) constructor
- * because it allows for reuse of frequently used BigDecimals (like
- * 0 and 1), obviating the need for exported constants.
- */
- public static BigDecimal valueOf(long val) {
- return valueOf(val, 0);
- }
-
-
- // Arithmetic Operations
-
- /**
- * Returns a BigDecimal whose value is (this + val), and whose scale is
- * MAX(this.scale(), val.scale).
- */
- public BigDecimal add(BigDecimal val){
- BigDecimal arg[] = new BigDecimal[2];
- arg[0] = this; arg[1] = val;
- matchScale(arg);
- return new BigDecimal(arg[0].intVal.add(arg[1].intVal), arg[0].scale);
- }
-
- /**
- * Returns a BigDecimal whose value is (this - val), and whose scale is
- * MAX(this.scale(), val.scale).
- */
- public BigDecimal subtract(BigDecimal val){
- BigDecimal arg[] = new BigDecimal[2];
- arg[0] = this; arg[1] = val;
- matchScale(arg);
- return new BigDecimal(arg[0].intVal.subtract(arg[1].intVal),
- arg[0].scale);
- }
-
- /**
- * Returns a BigDecimal whose value is (this * val), and whose scale is
- * this.scale() + val.scale.
- */
- public BigDecimal multiply(BigDecimal val){
- return new BigDecimal(intVal.multiply(val.intVal), scale+val.scale);
- }
-
- /**
- * Returns a BigDecimal whose value is (this / val), and whose scale
- * is as specified. If rounding must be performed to generate a
- * result with the given scale, the specified rounding mode is
- * applied. Throws an ArithmeticException if val == 0, scale < 0,
- * or the rounding mode is ROUND_UNNECESSARY and the specified scale
- * is insufficient to represent the result of the division exactly.
- * Throws an IllegalArgumentException if roundingMode does not
- * represent a valid rounding mode.
- */
- public BigDecimal divide(BigDecimal val, int scale, int roundingMode)
- throws ArithmeticException, IllegalArgumentException {
- if (scale < 0)
- throw new ArithmeticException("Negative scale");
- if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
- throw new IllegalArgumentException("Invalid rounding mode");
-
- /*
- * Rescale dividend or divisor (whichever can be "upscaled" to
- * produce correctly scaled quotient).
- */
- BigDecimal dividend, divisor;
- if (scale + val.scale >= this.scale) {
- dividend = this.setScale(scale + val.scale);
- divisor = val;
- } else {
- dividend = this;
- divisor = val.setScale(this.scale - scale);
- }
-
- /* Do the division and return result if it's exact */
- BigInteger i[] = dividend.intVal.divideAndRemainder(divisor.intVal);
- BigInteger q = i[0], r = i[1];
- if (r.signum() == 0)
- return new BigDecimal(q, scale);
- else if (roundingMode == ROUND_UNNECESSARY) /* Rounding prohibited */
- throw new ArithmeticException("Rounding necessary");
-
- /* Round as appropriate */
- int signum = dividend.signum() * divisor.signum(); /* Sign of result */
- boolean increment;
- if (roundingMode == ROUND_UP) { /* Away from zero */
- increment = true;
- } else if (roundingMode == ROUND_DOWN) { /* Towards zero */
- increment = false;
- } else if (roundingMode == ROUND_CEILING) { /* Towards +infinity */
- increment = (signum > 0);
- } else if (roundingMode == ROUND_FLOOR) { /* Towards -infinity */
- increment = (signum < 0);
- } else { /* Remaining modes based on nearest-neighbor determination */
- int cmpFracHalf = r.abs().multiply(BigInteger.valueOf(2)).
- compareTo(divisor.intVal.abs());
- if (cmpFracHalf < 0) { /* We're closer to higher digit */
- increment = false;
- } else if (cmpFracHalf > 0) { /* We're closer to lower digit */
- increment = true;
- } else { /* We're dead-center */
- if (roundingMode == ROUND_HALF_UP)
- increment = true;
- else if (roundingMode == ROUND_HALF_DOWN)
- increment = false;
- else /* roundingMode == ROUND_HALF_EVEN */
- increment = q.testBit(0); /* true iff q is odd */
- }
- }
- return (increment
- ? new BigDecimal(q.add(BigInteger.valueOf(signum)), scale)
- : new BigDecimal(q, scale));
- }
-
- /**
- * Returns a BigDecimal whose value is (this / val), and whose scale
- * is this.scale(). If rounding must be performed to generate a
- * result with the given scale, the specified rounding mode is
- * applied. Throws an ArithmeticException if val == 0. Throws
- * an IllegalArgumentException if roundingMode does not represent a
- * valid rounding mode.
- */
- public BigDecimal divide(BigDecimal val, int roundingMode)
- throws ArithmeticException, IllegalArgumentException{
- return this.divide(val, scale, roundingMode);
- }
-
- /**
- * Returns a BigDecimal whose value is the absolute value of this
- * number, and whose scale is this.scale().
- */
- public BigDecimal abs(){
- return (signum() < 0 ? negate() : this);
- }
-
- /**
- * Returns a BigDecimal whose value is -1 * this, and whose scale is
- * this.scale().
- */
- public BigDecimal negate(){
- return new BigDecimal(intVal.negate(), scale);
- }
-
- /**
- * Returns the signum function of this number (i.e., -1, 0 or 1 as
- * the value of this number is negative, zero or positive).
- */
- public int signum(){
- return intVal.signum();
- }
-
- /**
- * Returns the scale of this number.
- */
- public int scale(){
- return scale;
- }
-
-
- // Rounding Modes
-
- /**
- * Always increment the digit prior to a non-zero discarded fraction.
- * Note that this rounding mode never decreases the magnitude.
- * (Rounds away from zero.)
- */
- public final static int ROUND_UP = 0;
-
- /**
- * Never increment the digit prior to a discarded fraction (i.e.,
- * truncate). Note that this rounding mode never increases the magnitude.
- * (Rounds towards zero.)
- */
- public final static int ROUND_DOWN = 1;
-
- /**
- * If the BigDecimal is positive, behave as for ROUND_UP; if negative,
- * behave as for ROUND_DOWN. Note that this rounding mode never decreases
- * the value. (Rounds towards positive infinity.)
- */
- public final static int ROUND_CEILING = 2;
-
- /**
- * If the BigDecimal is positive, behave as for ROUND_DOWN; if negative
- * behave as for ROUND_UP. Note that this rounding mode never increases
- * the value. (Rounds towards negative infinity.)
- */
- public final static int ROUND_FLOOR = 3;
-
- /**
- * Behave as for ROUND_UP if the discarded fraction is >= .5; otherwise,
- * behave as for ROUND_DOWN. (Rounds towards "nearest neighbor" unless
- * both neighbors are equidistant, in which case rounds up.)
- */
- public final static int ROUND_HALF_UP = 4;
-
- /**
- * Behave as for ROUND_UP if the discarded fraction is > .5; otherwise,
- * behave as for ROUND_DOWN. (Rounds towards "nearest neighbor" unless
- * both neighbors are equidistant, in which case rounds down.)
- */
- public final static int ROUND_HALF_DOWN = 5;
-
- /**
- * Behave as for ROUND_HALF_UP if the digit to the left of the discarded
- * fraction is odd; behave as for ROUND_HALF_DOWN if it's even. (Rounds
- * towards the "nearest neighbor" unless both neighbors are equidistant,
- * in which case, rounds towards the even neighbor.)
- */
- public final static int ROUND_HALF_EVEN = 6;
-
- /**
- * This "pseudo-rounding-mode" is actually an assertion that the requested
- * operation has an exact result, hence no rounding is necessary. If this
- * rounding mode is specified on an operation that yields an inexact result,
- * an arithmetic exception is thrown.
- */
- public final static int ROUND_UNNECESSARY = 7;
-
-
- // Scaling/Rounding Operations
-
- /**
- * Returns a BigDecimal whose scale is the specified value, and whose
- * integer value is determined by multiplying or dividing this BigDecimal's
- * integer value by the appropriate power of ten to maintain the overall
- * value. If the scale is reduced by the operation, the integer value
- * must be divided (rather than multiplied), and precision may be lost;
- * in this case, the specified rounding mode is applied to the division.
- * Throws an ArithmeticException if scale is negative, or the rounding
- * mode is ROUND_UNNECESSARY and it is impossible to perform the
- * specified scaling operation without loss of precision. Throws an
- * IllegalArgumentException if roundingMode does not represent a valid
- * rounding mode.
- */
- public BigDecimal setScale(int scale, int roundingMode)
- throws ArithmeticException, IllegalArgumentException {
- if (scale < 0)
- throw new ArithmeticException("Negative scale");
- if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
- throw new IllegalArgumentException("Invalid rounding mode");
-
- /* Handle the easy cases */
- if (scale == this.scale)
- return this;
- else if (scale > this.scale)
- return new BigDecimal(timesTenToThe(intVal, scale-this.scale),
- scale);
- else /* scale < this.scale */
- return divide(valueOf(1), scale, roundingMode);
- }
-
- /**
- * Returns a BigDecimal whose scale is the specified value, and whose
- * value is exactly equal to this number's. Throws an ArithmeticException
- * if this is not possible. This call is typically used to increase
- * the scale, in which case it is guaranteed that there exists a BigDecimal
- * of the specified scale and the correct value. The call can also be used
- * to reduce the scale if the caller knows that the number has sufficiently
- * many zeros at the end of its fractional part (i.e., factors of ten in
- * its integer value) to allow for the rescaling without loss of precision.
- * Note that this call returns the same result as the two argument version
- * of setScale, but saves the caller the trouble of specifying a rounding
- * mode in cases where it is irrelevant.
- */
- public BigDecimal setScale(int scale)
- throws ArithmeticException, IllegalArgumentException
- {
- return setScale(scale, ROUND_UNNECESSARY);
- }
-
-
- // Decimal Point Motion Operations
-
- /**
- * Returns a BigDecimal which is equivalent to this one with the decimal
- * point moved n places to the left. If n is non-negative, the call merely
- * adds n to the scale. If n is negative, the call is equivalent to
- * movePointRight(-n). (The BigDecimal returned by this call has value
- * (this * 10**-n) and scale MAX(this.scale()+n, 0).)
- */
- public BigDecimal movePointLeft(int n){
- return (n>=0 ? new BigDecimal(intVal, scale+n) : movePointRight(-n));
- }
-
- /**
- * Moves the decimal point the specified number of places to the right.
- * If this number's scale is >= n, the call merely subtracts n from the
- * scale; otherwise, it sets the scale to zero, and multiplies the integer
- * value by 10 ** (n - this.scale). If n is negative, the call is
- * equivalent to movePointLeft(-n). (The BigDecimal returned by this call
- * has value (this * 10**n) and scale MAX(this.scale()-n, 0).)
- */
- public BigDecimal movePointRight(int n){
- return (scale >= n ? new BigDecimal(intVal, scale-n)
- : new BigDecimal(timesTenToThe(intVal, n-scale),0));
- }
-
- // Comparison Operations
-
- /**
- * Returns -1, 0 or 1 as this number is less than, equal to, or greater
- * than val. Two BigDecimals that are equal in value but have a
- * different scale (e.g., 2.0, 2.00) are considered equal by this method.
- * This method is provided in preference to individual methods for each
- * of the six boolean comparison operators (<, ==, >, >=, !=,
- * <=). The suggested idiom for performing these comparisons is:
- * (x.compareTo(y) <op> 0), where <op> is one of the six
- * comparison operators.
- */
- public int compareTo(BigDecimal val){
- /* Optimization: would run fine without the next three lines */
- int sigDiff = signum() - val.signum();
- if (sigDiff != 0)
- return (sigDiff > 0 ? 1 : -1);
-
- /* If signs match, scale and compare intVals */
- BigDecimal arg[] = new BigDecimal[2];
- arg[0] = this; arg[1] = val;
- matchScale(arg);
- return arg[0].intVal.compareTo(arg[1].intVal);
- }
-
- /**
- * Compares this BigDecimal to another Object. If the Object is a
- * BigDecimal, this function behaves like
- * <code>compareTo(BigDecimal)</code>. Otherwise, it throws a
- * <code>ClassCastException</code> (as BigDecimals are comparable
- * only to other BigDecimals).
- *
- * @param o the <code>Object</code> to be compared.
- * @return the value <code>0</code> if the argument is a BigDecimal
- * numerically equal to this BigDecimal; a value less than
- * <code>0</code> if the argument is a BigDecimal numerically
- * greater than this BigDecimal; and a value greater than
- * <code>0</code> if the argument is a BigDecimal numerically
- * less than this BigDecimal.
- * @exception <code>ClassCastException</code> if the argument is not a
- * <code>BigDecimal</code>.
- * @see java.lang.Comparable
- * @since JDK1.2
- */
- public int compareTo(Object o) {
- return compareTo((BigDecimal)o);
- }
-
- /**
- * Returns true iff x is a BigDecimal whose value is equal to this number.
- * This method is provided so that BigDecimals can be used as hash keys.
- * Unlike compareTo, this method considers two BigDecimals equal only
- * if they are equal in value and scale.
- */
- public boolean equals(Object x){
- if (!(x instanceof BigDecimal))
- return false;
- BigDecimal xDec = (BigDecimal) x;
-
- return scale == xDec.scale && intVal.equals(xDec.intVal);
- }
-
- /**
- * Returns the BigDecimal whose value is the lesser of this and val.
- * If the values are equal (as defined by the compareTo operator),
- * either may be returned.
- */
- public BigDecimal min(BigDecimal val){
- return (compareTo(val)<0 ? this : val);
- }
-
- /**
- * Returns the BigDecimal whose value is the greater of this and val.
- * If the values are equal (as defined by the compareTo operator),
- * either may be returned.
- */
- public BigDecimal max(BigDecimal val){
- return (compareTo(val)>0 ? this : val);
- }
-
-
- // Hash Function
-
- /**
- * Computes a hash code for this object. Note that two BigDecimals
- * that are numerically equal but differ in scale (e.g., 2.0, 2.00) will
- * not generally have the same hash code.
- */
- public int hashCode(){
- return 31*intVal.hashCode() + scale;
- }
-
- // Format Converters
-
- /**
- * Returns the string representation of this number. The digit-to-
- * character mapping provided by Character.forDigit is used. The minus
- * sign and decimal point are used to indicate sign and scale. (This
- * representation is compatible with the (String, int) constructor.)
- */
- public String toString(){
- if (scale == 0) /* No decimal point */
- return intVal.toString();
-
- /* Insert decimal point */
- StringBuffer buf;
- String intString = intVal.abs().toString();
- int signum = signum();
- int insertionPoint = intString.length() - scale;
- if (insertionPoint == 0) { /* Point goes right before intVal */
- return (signum<0 ? "-0." : "0.") + intString;
- } else if (insertionPoint > 0) { /* Point goes inside intVal */
- buf = new StringBuffer(intString);
- buf.insert(insertionPoint, '.');
- if (signum < 0)
- buf.insert(0, '-');
- } else { /* We must insert zeros between point and intVal */
- buf = new StringBuffer(3-insertionPoint + intString.length());
- buf.append(signum<0 ? "-0." : "0.");
- for (int i=0; i<-insertionPoint; i++)
- buf.append('0');
- buf.append(intString);
- }
- return buf.toString();
- }
-
- /**
- * Converts this number to a BigInteger. Standard narrowing primitive
- * conversion as per The Java Language Specification. In particular,
- * note that any fractional part of this number will be truncated.
- */
- public BigInteger toBigInteger(){
- return (scale==0 ? intVal
- : intVal.divide(BigInteger.valueOf(10).pow(scale)));
- }
-
- /**
- * Converts this number to an int. Standard narrowing primitive conversion
- * as per The Java Language Specification. In particular, note that any
- * fractional part of this number will be truncated.
- */
- public int intValue(){
- return toBigInteger().intValue();
- }
-
- /**
- * Converts this number to a long. Standard narrowing primitive conversion
- * as per The Java Language Specification. In particular, note that any
- * fractional part of this number will be truncated.
- */
- public long longValue(){
- return toBigInteger().longValue();
- }
-
- /**
- * Converts this number to a float. Similar to the double-to-float
- * narrowing primitive conversion defined in The Java Language
- * Specification: if the number has too great a magnitude to represent
- * as a float, it will be converted to infinity or negative infinity,
- * as appropriate.
- */
- public float floatValue(){
- /* Somewhat inefficient, but guaranteed to work. */
- return Float.valueOf(this.toString()).floatValue();
- }
-
- /**
- * Converts the number to a double. Similar to the double-to-float
- * narrowing primitive conversion defined in The Java Language
- * Specification: if the number has too great a magnitude to represent
- * as a double, it will be converted to infinity or negative infinity,
- * as appropriate.
- */
- public double doubleValue(){
- /* Somewhat inefficient, but guaranteed to work. */
- return Double.valueOf(this.toString()).doubleValue();
- }
-
-
- // Private "Helper" Methods
-
- /* Returns (a * 10^b) */
- private static BigInteger timesTenToThe(BigInteger a, int b) {
- return a.multiply(BigInteger.valueOf(10).pow(b));
- }
-
- /*
- * If the scales of val[0] and val[1] differ, rescale (non-destructively)
- * the lower-scaled BigDecimal so they match.
- */
- private static void matchScale(BigDecimal[] val) {
- if (val[0].scale < val[1].scale)
- val[0] = val[0].setScale(val[1].scale);
- else if (val[1].scale < val[0].scale)
- val[1] = val[1].setScale(val[0].scale);
- }
- }
-