I commited this: 2006-07-27 Sven de Marothy <> * java/math/BigDecimal.java Adjust copyright date. (divide(BigDecimal): Implement. (precision): Reimplement. (numDigitsInBigInteger, numDigitsInLong): Removed. (toString): Get exponent from string length, fix negative values with exponential form. (toEngineeringString): Same as for toString. (setScale): Throw ArithmeticException if scale < 0. This fixes all the BigDecimal regressions, with the exception of one in BigDecimal.construct, which isn't a regression, just that the string representation returned from toString() changed in this case from "1000" to "1E+3", which is also what the 1.5 JRE returns (but not 1.4). Either value is still allowed by the spec, so it's more a case of a too rigid test. We need a lot more tests for this, in particular for the new 1.5 methods. The setScale() regression may not be one, as the 1.5 JRE no longer throws ArithmeticException with a negative scale here, despite the 1.5 docs still saying that it does. (1.4 and earlier do throw the exception). I'm thinking this is a Sun regression in 1.5. But if it's still around in 1.6. we should probably change. /Sven
Index: java/math/BigDecimal.java =================================================================== RCS file: /sources/classpath/classpath/java/math/BigDecimal.java,v retrieving revision 1.23 diff -U3 -r1.23 BigDecimal.java --- java/math/BigDecimal.java 7 Jun 2006 19:01:07 -0000 1.23 +++ java/math/BigDecimal.java 28 Jul 2006 08:28:35 -0000 @@ -1,5 +1,5 @@ /* java.math.BigDecimal -- Arbitrary precision decimals. - Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -266,8 +266,10 @@ 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; @@ -748,6 +750,19 @@ } /** + * Performs division, if the resulting quotient requires rounding + * (has a nonterminating decimal expansion), + * an ArithmeticException is thrown. + * #see divide(BigDecimal, int, int) + * @since 1.5 + */ + public BigDecimal divide(BigDecimal divisor) + throws ArithmeticException, IllegalArgumentException + { + return divide(divisor, scale, ROUND_UNNECESSARY); + } + + /** * Returns a BigDecimal whose value is the remainder in the quotient * this / val. This is obtained by * subtract(divideToIntegralValue(val).multiply(val)). @@ -760,7 +775,7 @@ { 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 @@ -994,84 +1009,13 @@ { if (precision == 0) { - if (intVal.compareTo(BigInteger.TEN.pow(18)) == 1) - precision = numDigitsInBigInteger(intVal); - else - precision = numDigitsInLong(intVal.longValue()); - } + String s = intVal.toString(); + precision = s.length() - (( s.charAt(0) == '-' ) ? 1 : 0); + } 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: @@ -1097,15 +1041,14 @@ if (scale == 0) return bigStr; - // This is the adjusted exponent described above. - int adjExp = -scale + (numDigitsInLong(intVal.longValue()) - 1); + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + StringBuilder val = new StringBuilder(); - if (scale >= 0 && adjExp >= -6) + if (scale >= 0 && (point - 1) >= -6) { - // Convert to character form without scientific notation. - boolean negative = (bigStr.charAt(0) == '-'); - int point = bigStr.length() - scale - (negative ? 1 : 0); + // Convert to character form without scientific notation. if (point <= 0) { // Zeros need to be prepended to the StringBuilder. @@ -1136,12 +1079,12 @@ // 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.insert( ( negative ? 2 : 1 ), '.'); + // And then append 'E' and the exponent = (point - 1). val.append('E'); - if (adjExp >= 0) + if (point - 1 >= 0) val.append('+'); - val.append(adjExp); + val.append( point - 1 ); } return val.toString(); } @@ -1163,15 +1106,16 @@ if (scale == 0) return bigStr; + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + // This is the adjusted exponent described above. - int adjExp = -scale + (numDigitsInLong(intVal.longValue()) - 1); + int adjExp = point - 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. @@ -1238,7 +1182,7 @@ val.append('0'); } else if (bigStr.length() > dot) - val.insert(dot, '.'); + val.insert(dot + (negative ? 1 : 0), '.'); // And then append 'E' and the exponent (adjExp). val.append('E'); @@ -1400,6 +1344,10 @@ public BigDecimal setScale (int scale, int roundingMode) throws ArithmeticException, IllegalArgumentException { + // NOTE: The 1.5 JRE doesn't throw this, ones prior to it do and + // the spec says it should. Nevertheless, if 1.6 doesn't fix this + // we should consider removing it. + if( scale < 0 ) throw new ArithmeticException("Scale parameter < 0."); return divide (ONE, scale, roundingMode); }