[java] Fix BigDecimal ROUND_HALF_EVEN

Jerry Quinn jlquinn@optonline.net
Wed Jun 11 04:04:00 GMT 2003


Per Bothner writes:
 > Tom Tromey wrote:
 > 
 > >>>>>>"Jerry" == Jerry Quinn <jlquinn@optonline.net> writes:
 > > 
 > > 
 > > Jerry> The following program should output 0.21 after scaling, but
 > > Jerry> outputs 0.20.  The attached patch fixes the bug.
 > > 
 > > I don't know this code very well, so I'm reluctant to approve the
 > > patch.  Per, could you look at it?
 > 
 > I can't say I understand the code very well either.
 > The test for 'roundDigit > 5' in case ROUND_HALF_DOWN
 > looks suspicious also.

Yup.  I just went back and looked at it again.  In the current style,
it should probably read:

case ROUND_HALF_DOWN:
  roundingMode = (roundDigit > 5 || (roundDigit == 5 && parts[1].signum() != 0))
	? ROUND_UP : ROUND_DOWN;

 > It might be possible to simplify and optimize the code.
 > I'd remove the "+ 1" in the setting of 'power', and
 > then just set unrounded to to parts[0].  I'd drop
 > roundDigit, whihc which makes me feel very uncomfortable,
 > and just compare the remainder (parts[1]) with valIntVal:
 > 
 > For simplicity, assume all the numbers are positive.
 > Then for ROUND_FLOOR or ROUND_DOWN, ignore the remainer.
 > Then for ROUND_CEILING, ROUND_UP, add one to unrounded
 > iff the remainder is non-zero.
 > 
 > Otherwise, double the remainder, and compare it with valIntVal.
 > If the doubled remainder is less than valIntVal, ignore it.
 > If it is greater, treat as ROUND_UP.  If it is equal,
 > it depends on the rounding mode in the obvious manner.
 > 
 > Adjust as appropriate if this and/or val are negative.
 > 
 > Does this logic seem correct?  If so, do we have a volunteer
 > to try implementing (and testing) it?

It seems reasonable.  Here's an implementation.  I've tried to install
the Mauve testsuite with no luck.  The snapshot on the website is
missing configure and configure.in.  Mindless (the only level I know
right now) running of automake and autoconf don't produce working
results.

Anyway, here is the patch.

Jerry Quinn

*** BigDecimal.java.~1.9.~	Sat Apr 19 15:26:41 2003
--- BigDecimal.java	Wed Jun 11 00:01:00 2003
***************
*** 273,279 ****
      // 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
--- 273,279 ----
      // Ensure that pow gets a non-negative value.
      int valScale = val.scale;
      BigInteger valIntVal = val.intVal;
!     int power = newScale - (scale - val.scale);
      if (power < 0)
        {
  	// Effectively increase the scale of val to avoid an
***************
*** 288,297 ****
  //      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 ();
--- 288,301 ----
  //      System.out.println("int: " + parts[0]);
  //      System.out.println("rem: " + parts[1]);
  
!     BigInteger unrounded = parts[0];
  
!     // round is -1 if remainder*2 < parts[1], 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.
!     int half = parts[1].multiply(BigInteger.valueOf(2)).compareTo(valIntVal);
!     
!     if (half == 0 && parts[1].signum () == 0) // no rounding necessary
        return new BigDecimal (unrounded, newScale);
  
      int sign = unrounded.signum ();
***************
*** 307,329 ****
  	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;
        }
  
--- 311,330 ----
  	roundingMode = (sign == 1) ? ROUND_DOWN : ROUND_UP;
  	break;
        case ROUND_HALF_UP:
! 	roundingMode = (half == -1) ? ROUND_DOWN : ROUND_UP;
  	break;
        case ROUND_HALF_DOWN:
! 	roundingMode = (half == 1) ? ROUND_UP : ROUND_DOWN;
  	break;
        case ROUND_HALF_EVEN:
! 	if (half == -1)
! 	  roundingMode = ROUND_DOWN;
! 	else if (half == 1)
! 	  roundingMode = ROUND_UP;
! 	else if (unrounded.testBit(0)) // odd, then ROUND_HALF_UP
! 	  roundingMode = ROUND_UP;
! 	else			// even, ROUND_HALF_DOWN
  	  roundingMode = ROUND_DOWN;
  	break;
        }
  



More information about the Gcc-patches mailing list