diff options
Diffstat (limited to 'java/math/BigDecimal.java')
-rw-r--r-- | java/math/BigDecimal.java | 1125 |
1 files changed, 1079 insertions, 46 deletions
diff --git a/java/math/BigDecimal.java b/java/math/BigDecimal.java index 94b373b04..a58fa931b 100644 --- a/java/math/BigDecimal.java +++ b/java/math/BigDecimal.java @@ -1,5 +1,5 @@ /* java.math.BigDecimal -- Arbitrary precision decimals. - Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,7 @@ public class BigDecimal extends Number implements Comparable { private BigInteger intVal; private int scale; + private int precision = 0; private static final long serialVersionUID = 6108874887143696463L; /** @@ -48,21 +49,21 @@ public class BigDecimal extends Number implements Comparable * @since 1.5 */ public static final BigDecimal ZERO = - new BigDecimal (BigInteger.valueOf (0), 0); + new BigDecimal (BigInteger.ZERO, 0); /** * The constant one as a BigDecimal with scale zero. * @since 1.5 */ public static final BigDecimal ONE = - new BigDecimal (BigInteger.valueOf (1), 0); + new BigDecimal (BigInteger.ONE, 0); /** * The constant ten as a BigDecimal with scale zero. * @since 1.5 */ public static final BigDecimal TEN = - new BigDecimal (BigInteger.valueOf (10), 0); + new BigDecimal (BigInteger.TEN, 0); public static final int ROUND_UP = 0; public static final int ROUND_DOWN = 1; @@ -73,19 +74,181 @@ public class BigDecimal extends Number implements Comparable public static final int ROUND_HALF_EVEN = 6; public static final int ROUND_UNNECESSARY = 7; + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + * @since 1.5 + */ + public BigDecimal (int val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal using the BigDecimal(int) constructor and then + * rounds according to the MathContext. + * @param val the value for the initial (unrounded) BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (int val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + */ + public BigDecimal (long val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal from the long in the same way as BigDecimal(long) + * and then rounds according to the MathContext. + * @param val the long from which we create the initial BigDecimal + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (long val, MathContext mc) + { + this(val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose value is given by num rounded according to + * mc. Since num is already a BigInteger, the rounding refers only to the + * precision setting in mc, if mc.getPrecision() returns an int lower than + * the number of digits in num, then rounding is necessary. + * @param num the unscaledValue, before rounding + * @param mc the MathContext that specifies the precision + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * * @since 1.5 + */ + public BigDecimal (BigInteger num, MathContext mc) + { + this (num, 0); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal from the String val according to the same + * rules as the BigDecimal(String) constructor and then rounds + * according to the MathContext mc. + * @param val the String from which we construct the initial BigDecimal + * @param mc the MathContext that specifies the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (String val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is zero. + * @param num the value of the new BigDecimal + */ public BigDecimal (BigInteger num) { this (num, 0); } - public BigDecimal (BigInteger num, int scale) throws NumberFormatException + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is scale. + * @param num + * @param scale + */ + public BigDecimal (BigInteger num, int scale) { - if (scale < 0) - throw new NumberFormatException ("scale of " + scale + " is < 0"); this.intVal = num; this.scale = scale; } + + /** + * Constructs a BigDecimal using the BigDecimal(BigInteger, int) + * constructor and then rounds according to the MathContext. + * @param num the unscaled value of the unrounded BigDecimal + * @param scale the scale of the unrounded BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (BigInteger num, int scale, MathContext mc) + { + this (num, scale); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + /** + * Constructs a BigDecimal in the same way as BigDecimal(double) and then + * rounds according to the MathContext. + * @param num the double from which the initial BigDecimal is created + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (double num, MathContext mc) + { + this (num); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + public BigDecimal (double num) throws NumberFormatException { if (Double.isInfinite (num) || Double.isNaN (num)) @@ -136,6 +299,209 @@ public class BigDecimal extends Number implements Comparable } } + /** + * Constructs a BigDecimal from the char subarray and rounding + * according to the MathContext. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @param mc the MathContext for rounding + * @throws NumberFormatException if the char subarray is not a valid + * BigDecimal representation + * @throws ArithmeticException if the result is inexact but the rounding + * mode is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len, MathContext mc) + { + this(in, offset, len); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the char array and rounding according + * to the MathContext. + * @param in the char array + * @param mc the MathContext + * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal + * representation + * @throws ArithmeticException if the result is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, MathContext mc) + { + this(in, 0, in.length); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the given char array, accepting the same + * sequence of characters as the BigDecimal(String) constructor. + * @param in the char array + * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal + * representation + * @since 1.5 + */ + public BigDecimal(char[] in) + { + this(in, 0, in.length); + } + + /** + * Constructs a BigDecimal from a char subarray, accepting the same sequence + * of characters as the BigDecimal(String) constructor. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @throws NumberFormatException if <code>in</code> is not a valid + * BigDecimal representation. + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len) + { + // start is the index into the char array where the significand starts + int start = offset; + // end is one greater than the index of the last character used + int end = offset + len; + // point is the index into the char array where the exponent starts + // (or, if there is no exponent, this is equal to end) + int point = offset; + // dot is the index into the char array where the decimal point is + // found, or -1 if there is no decimal point + int dot = -1; + + // The following examples show what these variables mean. Note that + // point and dot don't yet have the correct values, they will be + // properly assigned in a loop later on in this method. + // + // Example 1 + // + // + 1 0 2 . 4 6 9 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = 6, point = end = 10 + // + // Example 2 + // + // + 2 3 4 . 6 1 3 E - 1 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 11, start = 3, dot = 6, point = 10, end = 13 + // + // Example 3 + // + // - 1 2 3 4 5 e 7 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = -1, point = 8, end = 10 + + // Determine the sign of the number. + boolean negative = false; + if (in[offset] == '+') + { + ++start; + ++point; + } + else if (in[offset] == '-') + { + ++start; + ++point; + negative = true; + } + + // Check each character looking for the decimal point and the + // start of the exponent. + while (point < end) + { + char c = in[point]; + if (c == '.') + { + // If dot != -1 then we've seen more than one decimal point. + if (dot != -1) + throw new NumberFormatException("multiple `.'s in number"); + dot = point; + } + // Break when we reach the start of the exponent. + else if (c == 'e' || c == 'E') + break; + // Throw an exception if the character was not a decimal or an + // exponent and is not a digit. + else if (!Character.isDigit(c)) + throw new NumberFormatException("unrecognized character at " + point + + ": " + c); + ++point; + } + + // val is a StringBuilder from which we'll create a BigInteger + // which will be the unscaled value for this BigDecimal + StringBuilder val = new StringBuilder(point - start - 1); + if (dot != -1) + { + // If there was a decimal we must combine the two parts that + // contain only digits and we must set the scale properly. + val.append(in, start, dot - start); + val.append(in, dot + 1, point - dot - 1); + scale = point - 1 - dot; + } + else + { + // If there was no decimal then the unscaled value is just the number + // formed from all the digits and the scale is zero. + val.append(in, start, point - start); + scale = 0; + } + if (val.length() == 0) + throw new NumberFormatException("no digits seen"); + + // Prepend a negative sign if necessary. + if (negative) + val.insert(0, '-'); + intVal = new BigInteger(val.toString()); + + // Now parse exponent. + // If point < end that means we broke out of the previous loop when we + // saw an 'e' or an 'E'. + if (point < end) + { + point++; + // Ignore a '+' sign. + if (in[point] == '+') + point++; + + // Throw an exception if there were no digits found after the 'e' + // or 'E'. + if (point >= end) + throw new NumberFormatException("no exponent following e or E"); + + try + { + // Adjust the scale according to the exponent. + // Remember that the value of a BigDecimal is + // unscaledValue x Math.pow(10, -scale) + scale -= Integer.parseInt(new String(in, point, end - point)); + } + catch (NumberFormatException ex) + { + throw new NumberFormatException("malformed exponent"); + } + } + } + public BigDecimal (String num) throws NumberFormatException { int len = num.length(); @@ -199,18 +565,8 @@ public class BigDecimal extends Number implements Comparable throw new NumberFormatException ("no exponent following e or E"); try - { - int exp = Integer.parseInt (num.substring (point)); - exp -= scale; - if (signum () == 0) - scale = 0; - else if (exp > 0) - { - intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp)); - scale = 0; - } - else - scale = - exp; + { + scale -= Integer.parseInt (num.substring (point)); } catch (NumberFormatException ex) { @@ -247,29 +603,74 @@ public class BigDecimal extends Number implements Comparable BigInteger op1 = intVal; BigInteger op2 = val.intVal; if (scale < val.scale) - op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale)); + op1 = op1.multiply (BigInteger.TEN.pow (val.scale - scale)); else if (scale > val.scale) - op2 = op2.multiply (BigInteger.valueOf (10).pow (scale - val.scale)); + op2 = op2.multiply (BigInteger.TEN.pow (scale - val.scale)); return new BigDecimal (op1.add (op2), Math.max (scale, val.scale)); } + + /** + * Returns a BigDecimal whose value is found first by calling the + * method add(val) and then by rounding according to the MathContext mc. + * @param val the augend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return <code>this</code> + <code>val</code>, rounded if need be + * @since 1.5 + */ + public BigDecimal add (BigDecimal val, MathContext mc) + { + return add(val).round(mc); + } public BigDecimal subtract (BigDecimal val) { return this.add(val.negate()); } + /** + * Returns a BigDecimal whose value is found first by calling the + * method subtract(val) and then by rounding according to the MathContext mc. + * @param val the subtrahend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return <code>this</code> - <code>val</code>, rounded if need be + * @since 1.5 + */ + public BigDecimal subtract (BigDecimal val, MathContext mc) + { + return subtract(val).round(mc); + } + public BigDecimal multiply (BigDecimal val) { return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); } + + /** + * Returns a BigDecimal whose value is (this x val) before it is rounded + * according to the MathContext mc. + * @param val the multiplicand + * @param mc the MathContext for rounding + * @return a new BigDecimal with value approximately (this x val) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal multiply (BigDecimal val, MathContext mc) + { + return multiply(val).round(mc); + } 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 { @@ -277,9 +678,6 @@ public class BigDecimal extends Number implements Comparable 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 newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale); @@ -290,11 +688,11 @@ public class BigDecimal extends Number implements Comparable { // Effectively increase the scale of val to avoid an // ArithmeticException for a negative power. - valIntVal = valIntVal.multiply (BigInteger.valueOf (10).pow (-power)); + valIntVal = valIntVal.multiply (BigInteger.TEN.pow (-power)); power = 0; } - BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (power)); + BigInteger dividend = intVal.multiply (BigInteger.TEN.pow (power)); BigInteger parts[] = dividend.divideAndRemainder (valIntVal); @@ -303,7 +701,7 @@ public class BigDecimal extends Number implements Comparable return new BigDecimal (unrounded, newScale); if (roundingMode == ROUND_UNNECESSARY) - throw new ArithmeticException ("newScale is not large enough"); + throw new ArithmeticException ("Rounding necessary"); int sign = intVal.signum () * valIntVal.signum (); @@ -348,16 +746,78 @@ public class BigDecimal extends Number implements Comparable // roundingMode == ROUND_DOWN return new BigDecimal (unrounded, newScale); } + + /** + * Returns a BigDecimal whose value is the remainder in the quotient + * this / val. This is obtained by + * subtract(divideToIntegralValue(val).multiply(val)). + * @param val the divisor + * @return a BigDecimal whose value is the remainder + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal remainder(BigDecimal val) + { + return subtract(divideToIntegralValue(val).multiply(val)); + } + + /** + * Returns a BigDecimal array, the first element of which is the integer part + * of this / val, and the second element of which is the remainder of + * that quotient. + * @param val the divisor + * @return the above described BigDecimal array + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal[] divideAndRemainder(BigDecimal val) + { + BigDecimal[] result = new BigDecimal[2]; + result[0] = divideToIntegralValue(val); + result[1] = subtract(result[0].multiply(val)); + return result; + } + + /** + * Returns a BigDecimal whose value is the integer part of the quotient + * this / val. The preferred scale is this.scale - val.scale. + * @param val the divisor + * @return a BigDecimal whose value is the integer part of this / val. + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal divideToIntegralValue(BigDecimal val) + { + return divide(val, ROUND_DOWN).floor().setScale(scale - val.scale, ROUND_DOWN); + } + + /** + * Mutates this BigDecimal into one with no fractional part, whose value is + * equal to the largest integer that is <= to this BigDecimal. Note that + * since this method is private it is okay to mutate this BigDecimal. + * @return the BigDecimal obtained through the floor operation on this + * BigDecimal. + */ + private BigDecimal floor() + { + if (scale <= 0) + return this; + String intValStr = intVal.toString(); + intValStr = intValStr.substring(0, intValStr.length() - scale); + intVal = new BigInteger(intValStr).multiply(BigInteger.TEN.pow(scale)); + return this; + } - public int compareTo (BigDecimal val) + public int compareTo (Object obj) { + BigDecimal val = (BigDecimal) obj; if (scale == val.scale) return intVal.compareTo (val.intVal); BigInteger thisParts[] = - intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); + intVal.divideAndRemainder (BigInteger.TEN.pow (scale)); BigInteger valParts[] = - val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); + val.intVal.divideAndRemainder (BigInteger.TEN.pow (val.scale)); int compare; if ((compare = thisParts[0].compareTo (valParts[0])) != 0) @@ -377,11 +837,6 @@ public class BigDecimal extends Number implements Comparable return thisParts[1].compareTo (valParts[1]); } - public int compareTo (Object val) - { - return(compareTo((BigDecimal)val)); - } - public boolean equals (Object o) { return (o instanceof BigDecimal @@ -430,7 +885,7 @@ public class BigDecimal extends Number implements Comparable return new BigDecimal (intVal, scale - n); return new BigDecimal (intVal.multiply - (BigInteger.valueOf (10).pow (n - scale)), 0); + (BigInteger.TEN.pow (n - scale)), 0); } public int signum () @@ -457,21 +912,367 @@ public class BigDecimal extends Number implements Comparable { return new BigDecimal (intVal.negate (), scale); } + + /** + * Returns a BigDecimal whose value is found first by negating this via + * the negate() method, then by rounding according to the MathContext mc. + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is approximately (-this) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal negate(MathContext mc) + { + BigDecimal result = negate(); + if (mc.getPrecision() != 0) + result = result.round(mc); + return result; + } + + /** + * Returns this BigDecimal. This is included for symmetry with the + * method negate(). + * @return this + * @since 1.5 + */ + public BigDecimal plus() + { + return this; + } + + /** + * Returns a BigDecimal whose value is found by rounding <code>this</code> + * according to the MathContext. This is the same as round(MathContext). + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is <code>this</code> before being rounded + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal plus(MathContext mc) + { + return round(mc); + } + + /** + * Returns a BigDecimal which is this BigDecimal rounded according to the + * MathContext rounding settings. + * @param mc the MathContext that tells us how to round + * @return the rounded BigDecimal + */ + public BigDecimal round(MathContext mc) + { + int mcPrecision = mc.getPrecision(); + int numToChop = precision() - mcPrecision; + // If mc specifies not to chop any digits or if we've already chopped + // enough digits (say by using a MathContext in the constructor for this + // BigDecimal) then just return this. + if (mcPrecision == 0 || numToChop <= 0) + return this; + + // Make a new BigDecimal which is the correct power of 10 to chop off + // the required number of digits and then call divide. + BigDecimal div = new BigDecimal(BigInteger.TEN.pow(numToChop)); + BigDecimal rounded = divide(div, scale, 4); + rounded.scale -= numToChop; + rounded.precision = mcPrecision; + return rounded; + } + + /** + * Returns the precision of this BigDecimal (the number of digits in the + * unscaled value). The precision of a zero value is 1. + * @return the number of digits in the unscaled value, or 1 if the value + * is zero. + */ + public int precision() + { + if (precision == 0) + { + if (intVal.compareTo(BigInteger.TEN.pow(18)) == 1) + precision = numDigitsInBigInteger(intVal); + else + precision = numDigitsInLong(intVal.longValue()); + } + return precision; + } + + /** + * This method is used to determine the precision of BigIntegers with 19 or + * more digits. + * @param b the BigInteger + * @return the number of digits in <code>b</code> + */ + int numDigitsInBigInteger(BigInteger b) + { + int i = 19; + BigInteger comp = BigInteger.TEN.pow(i); + while (b.compareTo(comp) >= 0) + comp = BigInteger.TEN.pow(++i); + + return i; + } + + /** + * This method determines the number of digits in the long value l. + * @param l1 the long value + * @return the number of digits in l + */ + private static int numDigitsInLong(long l1) + { + long l = l1 >= 0 ? l1 : -l1; + // We divide up the range in a binary fashion, this first if + // takes care of numbers with 1 to 9 digits. + if (l < 1000000000L) + { + // This if is for numbers with 1 to 5 digits. + if (l < 100000L) + { + if (l < 100L) + return (l < 10L) ? 1 : 2; + if (l < 10000L) + return (l < 1000L) ? 3 : 4; + return 5; + } + // Here we handle numbers with 6 to 9 digits. + if (l < 10000000L) + return (l < 1000000L) ? 6 : 7; + return (l < 100000000L) ? 8 : 9; + } + // If we are at this point that means we didn't enter the loop for + // numbers with 1 to 9 digits, so our number has 10 to 19 digits. + // This first if handles numbers with 10 to 14 digits. + if (l < 100000000000000L) + { + // This handles numbers with 10 to 12 digits. + if (l < 1000000000000L) + { + if (l < 100000000000L) + return (l < 10000000000L) ? 10 : 11; + return 12; + } + // This handles numbers with 13 or 14 digits. + return (l < 10000000000000L) ? 13 : 14; + } + // Finally we handle numbers with 15 to 19 digits. + if (l < 100000000000000000L) + { + // 15 to 17 digits. + if (l < 1000000000000000L) + return 15; + return (l < 10000000000000000L) ? 16 : 17; + } + // 18 or 19 digits. + return (l < 1000000000000000000L) ? 18 : 19; + } + + /** + * Returns the String representation of this BigDecimal, using scientific + * notation if necessary. The following steps are taken to generate + * the result: + * + * 1. the BigInteger unscaledValue's toString method is called and if + * <code>scale == 0<code> is returned. + * 2. an <code>int adjExp</code> is created which is equal to the negation + * of <code>scale</code> plus the number of digits in the unscaled value, + * minus one. + * 3. if <code>scale >= 0 && adjExp >= -6</code> then we represent this + * BigDecimal without scientific notation. A decimal is added if the + * scale is positive and zeros are prepended as necessary. + * 4. if scale is negative or adjExp is less than -6 we use scientific + * notation. If the unscaled value has more than one digit, a decimal + * as inserted after the first digit, the character 'E' is appended + * and adjExp is appended. + */ + public String toString() + { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + // This is the adjusted exponent described above. + int adjExp = -scale + (numDigitsInLong(intVal.longValue()) - 1); + StringBuilder val = new StringBuilder(); + + if (scale >= 0 && adjExp >= -6) + { + // Convert to character form without scientific notation. + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + val.append(bigStr); + // If there is more than one digit in the unscaled value we put a + // decimal after the first digit. + if (bigStr.length() > 1) + val.insert(1, '.'); + // And then append 'E' and the exponent (adjExp). + val.append('E'); + if (adjExp >= 0) + val.append('+'); + val.append(adjExp); + } + return val.toString(); + } - public String toString () + /** + * Returns the String representation of this BigDecimal, using engineering + * notation if necessary. This is similar to toString() but when exponents + * are used the exponent is made to be a multiple of 3 such that the integer + * part is between 1 and 999. + * + * @return a String representation of this BigDecimal in engineering notation + * @since 1.5 + */ + public String toEngineeringString() { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. String bigStr = intVal.toString(); - if (scale == 0) + if (scale == 0) return bigStr; + // This is the adjusted exponent described above. + int adjExp = -scale + (numDigitsInLong(intVal.longValue()) - 1); + StringBuilder val = new StringBuilder(); + + if (scale >= 0 && adjExp >= -6) + { + // Convert to character form without scientific notation. + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + // The exponent must be a multiple of 3 and the integer part + // must be between 1 and 999. + val.append(bigStr); + int zeros = adjExp % 3; + int dot = 1; + if (adjExp > 0) + { + // If the exponent is positive we just move the decimal to the + // right and decrease the exponent until it is a multiple of 3. + dot += zeros; + adjExp -= zeros; + } + else + { + // If the exponent is negative then we move the dot to the right + // and decrease the exponent (increase its magnitude) until + // it is a multiple of 3. Note that this is not adjExp -= zeros + // because the mod operator doesn't give us the distance to the + // correct multiple of 3. (-5 mod 3) is -2 but the distance from + // -5 to the correct multiple of 3 (-6) is 1, not 2. + if (zeros == -2) + { + dot += 1; + adjExp -= 1; + } + else if (zeros == -1) + { + dot += 2; + adjExp -= 2; + } + } + + // Either we have to append zeros because, for example, 1.1E+5 should + // be 110E+3, or we just have to put the decimal in the right place. + if (dot > val.length()) + { + while (dot > val.length()) + val.append('0'); + } + else if (bigStr.length() > dot) + val.insert(dot, '.'); + + // And then append 'E' and the exponent (adjExp). + val.append('E'); + if (adjExp >= 0) + val.append('+'); + val.append(adjExp); + } + return val.toString(); + } + + /** + * Returns a String representation of this BigDecimal without using + * scientific notation. This is how toString() worked for releases 1.4 + * and previous. Zeros may be added to the end of the String. For + * example, an unscaled value of 1234 and a scale of -3 would result in + * the String 1234000, but the toString() method would return + * 1.234E+6. + * @return a String representation of this BigDecimal + * @since 1.5 + */ + public String toPlainString() + { + // If the scale is zero we simply return the String representation of the + // unscaled value. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + // Remember if we have to put a negative sign at the start. boolean negative = (bigStr.charAt(0) == '-'); int point = bigStr.length() - scale - (negative ? 1 : 0); - StringBuffer sb = new StringBuffer(bigStr.length() + 2 + - (point <= 0 ? (-point + 1) : 0)); + StringBuffer sb = new StringBuffer(bigStr.length() + 2 + + (point <= 0 ? (-point + 1) : 0)); if (point <= 0) { + // We have to prepend zeros and a decimal point. if (negative) sb.append('-'); sb.append('0').append('.'); @@ -482,24 +1283,95 @@ public class BigDecimal extends Number implements Comparable } sb.append(bigStr.substring(negative ? 1 : 0)); } + else if (point < bigStr.length()) + { + // No zeros need to be prepended or appended, just put the decimal + // in the right place. + sb.append(bigStr); + sb.insert(point + (negative ? 1 : 0), '.'); + } else { - sb.append(bigStr); - sb.insert(point + (negative ? 1 : 0), '.'); + // We must append zeros instead of using scientific notation. + sb.append(bigStr); + for (int i = bigStr.length(); i < point; i++) + sb.append('0'); } return sb.toString(); } - + + /** + * Converts this BigDecimal to a BigInteger. Any fractional part will + * be discarded. + * @return a BigDecimal whose value is equal to floor[this] + */ public BigInteger toBigInteger () { - return scale == 0 ? intVal : - intVal.divide (BigInteger.valueOf (10).pow (scale)); + // If scale > 0 then we must divide, if scale > 0 then we must multiply, + // and if scale is zero then we just return intVal; + if (scale > 0) + return intVal.divide (BigInteger.TEN.pow (scale)); + else if (scale < 0) + return intVal.multiply(BigInteger.TEN.pow(-scale)); + return intVal; + } + + /** + * Converts this BigDecimal into a BigInteger, throwing an + * ArithmeticException if the conversion is not exact. + * @return a BigInteger whose value is equal to the value of this BigDecimal + * @since 1.5 + */ + public BigInteger toBigIntegerExact() + { + if (scale > 0) + { + // If we have to divide, we must check if the result is exact. + BigInteger[] result = + intVal.divideAndRemainder(BigInteger.TEN.pow(scale)); + if (result[1].equals(BigInteger.ZERO)) + return result[0]; + throw new ArithmeticException("No exact BigInteger representation"); + } + else if (scale < 0) + // If we're multiplying instead, then we needn't check for exactness. + return intVal.multiply(BigInteger.TEN.pow(-scale)); + // If the scale is zero we can simply return intVal. + return intVal; } public int intValue () { return toBigInteger ().intValue (); } + + /** + * Returns a BigDecimal which is numerically equal to this BigDecimal but + * with no trailing zeros in the representation. For example, if this + * BigDecimal has [unscaledValue, scale] = [6313000, 4] this method returns + * a BigDecimal with [unscaledValue, scale] = [6313, 1]. As another + * example, [12400, -2] would become [124, -4]. + * @return a numerically equal BigDecimal with no trailing zeros + */ + public BigDecimal stripTrailingZeros() + { + String intValStr = intVal.toString(); + int newScale = scale; + int pointer = intValStr.length() - 1; + // This loop adjusts pointer which will be used to give us the substring + // of intValStr to use in our new BigDecimal, and also accordingly + // adjusts the scale of our new BigDecimal. + while (intValStr.charAt(pointer) == '0') + { + pointer --; + newScale --; + } + // Create a new BigDecimal with the appropriate substring and then + // set its scale. + BigDecimal result = new BigDecimal(intValStr.substring(0, pointer + 1)); + result.scale = newScale; + return result; + } public long longValue () { @@ -526,4 +1398,165 @@ public class BigDecimal extends Number implements Comparable { return divide (ONE, scale, roundingMode); } + + /** + * Returns a new BigDecimal constructed from the BigDecimal(String) + * constructor using the Double.toString(double) method to obtain + * the String. + * @param val the double value used in Double.toString(double) + * @return a BigDecimal representation of val + * @throws NumberFormatException if val is NaN or infinite + * @since 1.5 + */ + public static BigDecimal valueOf(double val) + { + if (Double.isInfinite(val) || Double.isNaN(val)) + throw new NumberFormatException("argument cannot be NaN or infinite."); + return new BigDecimal(Double.toString(val)); + } + + /** + * Returns a BigDecimal whose numerical value is the numerical value + * of this BigDecimal multiplied by 10 to the power of <code>n</code>. + * @param n the power of ten + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal scaleByPowerOfTen(int n) + { + BigDecimal result = new BigDecimal(intVal, scale - n); + result.precision = precision; + return result; + } + + /** + * Returns a BigDecimal whose value is <code>this</code> to the power of + * <code>n</code>. + * @param n the power + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal pow(int n) + { + if (n < 0 || n > 999999999) + throw new ArithmeticException("n must be between 0 and 999999999"); + BigDecimal result = new BigDecimal(intVal.pow(n), scale * n); + return result; + } + + /** + * Returns a BigDecimal whose value is determined by first calling pow(n) + * and then by rounding according to the MathContext mc. + * @param n the power + * @param mc the MathContext + * @return the new BigDecimal + * @throws ArithmeticException if n < 0 or n > 999999999 or if the result is + * inexact but the rounding is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal pow(int n, MathContext mc) + { + // FIXME: The specs claim to use the X3.274-1996 algorithm. We + // currently do not. + return pow(n).round(mc); + } + + /** + * Returns a BigDecimal whose value is the absolute value of this BigDecimal + * with rounding according to the given MathContext. + * @param mc the MathContext + * @return the new BigDecimal + */ + public BigDecimal abs(MathContext mc) + { + BigDecimal result = abs(); + result = result.round(mc); + return result; + } + + /** + * Returns the size of a unit in the last place of this BigDecimal. This + * returns a BigDecimal with [unscaledValue, scale] = [1, this.scale()]. + * @return the size of a unit in the last place of <code>this</code>. + * @since 1.5 + */ + public BigDecimal ulp() + { + return new BigDecimal(BigInteger.ONE, scale); + } + + /** + * Converts this BigDecimal to a long value. + * @return the long value + * @throws ArithmeticException if rounding occurs or if overflow occurs + * @since 1.5 + */ + public long longValueExact() + { + // Set scale will throw an exception if rounding occurs. + BigDecimal temp = setScale(0, ROUND_UNNECESSARY); + BigInteger tempVal = temp.intVal; + // Check for overflow. + long result = intVal.longValue(); + if (tempVal.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 1 + || (result < 0 && signum() == 1) || (result > 0 && signum() == -1)) + throw new ArithmeticException("this BigDecimal is too " + + "large to fit into the return type"); + + return intVal.longValue(); + } + + /** + * Converts this BigDecimal into an int by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into an <code>int</code>. + * @return an int whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into an int. + * @since 1.5 + */ + public int intValueExact() + { + long temp = longValueExact(); + int result = (int)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into an int"); + return result; + } + + /** + * Converts this BigDecimal into a byte by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into a <code>byte</code>. + * @return a byte whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a byte. + * @since 1.5 + */ + public byte byteValueExact() + { + long temp = longValueExact(); + byte result = (byte)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a byte"); + return result; + } + + /** + * Converts this BigDecimal into a short by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into a <code>short</code>. + * @return a short whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a short. + * @since 1.5 + */ + public short shortValueExact() + { + long temp = longValueExact(); + short result = (short)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a short"); + return result; + } } |