diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 6ee557e0e42efcbdec1c4ca9021aeccdaf238528..51dc68723e6b87169d995c9fd730addd55e1f939 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,10 @@ +2003-08-01 Jerry Quinn <jlquinn@optonline.net> + Mark Wielaard <mark@klomp.org> + + * java/math/BigDecimal (divide): Correctly handle + ROUND_HALF_EVEN when amount is greater than 0.5. + Simplify and optimize code. + 2003-07-31 Tom Tromey <tromey@redhat.com> More for PR libgcj/11737: diff --git a/libjava/java/math/BigDecimal.java b/libjava/java/math/BigDecimal.java index fc99cf1f7d23c7dbff989ee62627e4bf965507ca..f9965add4c1643f6fd53b9ef9636e929c034738e 100644 --- a/libjava/java/math/BigDecimal.java +++ b/libjava/java/math/BigDecimal.java @@ -1,5 +1,5 @@ /* java.math.BigDecimal -- Arbitrary precision decimals. - Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -273,7 +273,7 @@ public class BigDecimal extends Number implements Comparable // Ensure that pow gets a non-negative value. int valScale = val.scale; BigInteger valIntVal = val.intVal; - int power = newScale + 1 - (scale - val.scale); + int power = newScale - (scale - val.scale); if (power < 0) { // Effectively increase the scale of val to avoid an @@ -285,50 +285,53 @@ public class BigDecimal extends Number implements Comparable 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 + BigInteger unrounded = parts[0]; + if (parts[1].signum () == 0) // no remainder, no rounding necessary return new BigDecimal (unrounded, newScale); - int sign = unrounded.signum (); + if (roundingMode == ROUND_UNNECESSARY) + throw new ArithmeticException ("newScale is not large enough"); + + int sign = intVal.signum () * valIntVal.signum (); - switch (roundingMode) + if (roundingMode == ROUND_CEILING) + roundingMode = (sign > 0) ? ROUND_UP : ROUND_DOWN; + else if (roundingMode == ROUND_FLOOR) + roundingMode = (sign < 0) ? ROUND_UP : ROUND_DOWN; + else { - 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 + // half is -1 if remainder*2 < positive intValue (*power), 0 if equal, + // 1 if >. This implies that the remainder to round is less than, + // equal to, or greater than half way to the next digit. + BigInteger posRemainder + = parts[1].signum () < 0 ? parts[1].negate() : parts[1]; + valIntVal = valIntVal.signum () < 0 ? valIntVal.negate () : valIntVal; + int half = posRemainder.shiftLeft(1).compareTo(valIntVal); + + switch(roundingMode) { - int rightmost = - unrounded.mod (BigInteger.valueOf (10)).intValue (); - if (rightmost % 2 == 1) // odd, then ROUND_HALF_UP + case ROUND_HALF_UP: + roundingMode = (half < 0) ? ROUND_DOWN : ROUND_UP; + break; + case ROUND_HALF_DOWN: + roundingMode = (half > 0) ? ROUND_UP : ROUND_DOWN; + break; + case ROUND_HALF_EVEN: + if (half < 0) + roundingMode = ROUND_DOWN; + else if (half > 0) + roundingMode = ROUND_UP; + else if (unrounded.testBit(0)) // odd, then ROUND_HALF_UP roundingMode = ROUND_UP; - else // even, then ROUND_HALF_DOWN - roundingMode = (roundDigit > 5) ? ROUND_UP : ROUND_DOWN; + else // even, ROUND_HALF_DOWN + roundingMode = ROUND_DOWN; + break; } - break; } if (roundingMode == ROUND_UP) - return new BigDecimal (unrounded.add (BigInteger.valueOf (1)), newScale); + unrounded = unrounded.add (BigInteger.valueOf (sign > 0 ? 1 : -1)); // roundingMode == ROUND_DOWN return new BigDecimal (unrounded, newScale);