Skip to content

Commit

Permalink
8314169: Combine related RoundingMode logic in j.text.DigitList
Browse files Browse the repository at this point in the history
Reviewed-by: naoto
  • Loading branch information
Justin Lu committed Aug 17, 2023
1 parent 808bb1f commit 96778dd
Showing 1 changed file with 85 additions and 102 deletions.
187 changes: 85 additions & 102 deletions src/java.base/share/classes/java/text/DigitList.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,21 @@ final class DigitList implements Cloneable {
* Return true if the represented number is zero.
*/
boolean isZero() {
for (int i=0; i < count; ++i) {
return !nonZeroAfterIndex(0);
}


/**
* Return true if there exists a non-zero digit in the digit list
* from the given index until the end.
*/
private boolean nonZeroAfterIndex(int index) {
for (int i=index; i < count; ++i) {
if (digits[i] != '0') {
return false;
return true;
}
}
return true;
return false;
}

/**
Expand Down Expand Up @@ -190,9 +199,7 @@ public final long getLong() {

StringBuilder temp = getStringBuilder();
temp.append(digits, 0, count);
for (int i = count; i < decimalAt; ++i) {
temp.append('0');
}
temp.append("0".repeat(Math.max(0, decimalAt - count)));
return Long.parseLong(temp.toString());
}

Expand Down Expand Up @@ -392,6 +399,17 @@ private void set(boolean isNegative, String s,

}

/**
* Round the representation to the given number of digits.
* @param maximumDigits The maximum number of digits to be shown.
*
* Upon return, count will be less than or equal to maximumDigits.
*/
private void roundInt(int maximumDigits) {
// Integers do not need to worry about double rounding
round(maximumDigits, false, true);
}

/**
* Round the representation to the given number of digits.
* @param maximumDigits The maximum number of digits to be shown.
Expand All @@ -408,25 +426,8 @@ private final void round(int maximumDigits,
// Round up if appropriate.
if (maximumDigits >= 0 && maximumDigits < count) {
if (shouldRoundUp(maximumDigits, alreadyRounded, valueExactAsDecimal)) {
// Rounding up involved incrementing digits from LSD to MSD.
// In most cases this is simple, but in a worst case situation
// (9999..99) we have to adjust the decimalAt value.
for (;;) {
--maximumDigits;
if (maximumDigits < 0) {
// We have all 9's, so we increment to a single digit
// of one and adjust the exponent.
digits[0] = '1';
++decimalAt;
maximumDigits = 0; // Adjust the count
break;
}

++digits[maximumDigits];
if (digits[maximumDigits] <= '9') break;
// digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
}
++maximumDigits; // Increment for use as count
// Rounding can adjust the max digits
maximumDigits = roundUp(maximumDigits);
}
count = maximumDigits;

Expand Down Expand Up @@ -508,94 +509,44 @@ private boolean shouldRoundUp(int maximumDigits,

switch(roundingMode) {
case UP:
for (int i=maximumDigits; i<count; ++i) {
if (digits[i] != '0') {
return true;
}
}
break;
return nonZeroAfterIndex(maximumDigits);
case DOWN:
break;
case CEILING:
for (int i=maximumDigits; i<count; ++i) {
if (digits[i] != '0') {
return !isNegative;
}
}
break;
return nonZeroAfterIndex(maximumDigits) && !isNegative;
case FLOOR:
for (int i=maximumDigits; i<count; ++i) {
if (digits[i] != '0') {
return isNegative;
}
}
break;
return nonZeroAfterIndex(maximumDigits) && isNegative;
case HALF_UP:
case HALF_DOWN:
if (digits[maximumDigits] > '5') {
// Value is above tie ==> must round up
return true;
} else if (digits[maximumDigits] == '5') {
// Digit at rounding position is a '5'. Tie cases.
if (maximumDigits != (count - 1)) {
// There are remaining digits. Above tie => must round up
return true;
} else {
// Digit at rounding position is the last one !
if (valueExactAsDecimal) {
// Exact binary representation. On the tie.
// Apply rounding given by roundingMode.
return roundingMode == RoundingMode.HALF_UP;
} else {
// Not an exact binary representation.
// Digit sequence either rounded up or truncated.
// Round up only if it was truncated.
return !alreadyRounded;
}
}
}
// Digit at rounding position is < '5' ==> no round up.
// Just let do the default, which is no round up (thus break).
break;
case HALF_EVEN:
// Implement IEEE half-even rounding
// Above tie, round up for all cases
if (digits[maximumDigits] > '5') {
return true;
// At tie, consider UP, DOWN, and EVEN logic
} else if (digits[maximumDigits] == '5' ) {
// Rounding position is the last index, there are 3 Cases.
if (maximumDigits == (count - 1)) {
// the rounding position is exactly the last index :
if (alreadyRounded)
// If FloatingDecimal rounded up (value was below tie),
// then we should not round up again.
return false;

if (!valueExactAsDecimal)
// Otherwise if the digits don't represent exact value,
// value was above tie and FloatingDecimal truncated
// digits to tie. We must round up.
return true;
else {
// This is an exact tie value, and FloatingDecimal
// provided all of the exact digits. We thus apply
// HALF_EVEN rounding rule.
return ((maximumDigits > 0) &&
(digits[maximumDigits-1] % 2 != 0));
// When exact, consider specific contract logic
if (valueExactAsDecimal) {
return (roundingMode == RoundingMode.HALF_UP) ||
(roundingMode == RoundingMode.HALF_EVEN
&& (maximumDigits > 0) && (digits[maximumDigits - 1] % 2 != 0));
// If already rounded, do not round again, otherwise round up
} else {
return !alreadyRounded;
}
// Rounding position is not the last index
// If any further digits have a non-zero value, round up
} else {
// Rounds up if it gives a non null digit after '5'
for (int i=maximumDigits+1; i<count; ++i) {
if (digits[i] != '0')
return true;
}
return nonZeroAfterIndex(maximumDigits+1);
}
}
// Below tie, do not round up for all cases
break;
case UNNECESSARY:
for (int i=maximumDigits; i<count; ++i) {
if (digits[i] != '0') {
throw new ArithmeticException(
if (nonZeroAfterIndex(maximumDigits)) {
throw new ArithmeticException(
"Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
}
}
break;
default:
Expand All @@ -605,6 +556,33 @@ private boolean shouldRoundUp(int maximumDigits,
return false;
}

/**
* Round the digit list up numerically.
* This involves incrementing digits from the LSD to the MSD.
* @param maximumDigits The maximum number of digits to be shown.
* @return The new maximum digits after rounding.
*/
private int roundUp(int maximumDigits) {
do {
--maximumDigits;
/*
* We have exhausted the max digits while attempting to round up
* from the LSD to the MSD. This implies a value of all 9's. As such,
* adjust representation to a single digit of one and increment the exponent.
*/
if (maximumDigits < 0) {
digits[0] = '1';
++decimalAt;
maximumDigits = 0; // Adjust the count
break;
}
++digits[maximumDigits];
}
while (digits[maximumDigits] > '9');

return ++maximumDigits; // Increment for use as count
}

/**
* Utility routine to set the value of the digit list from a long
*/
Expand Down Expand Up @@ -649,12 +627,16 @@ final void set(boolean isNegative, long source, int maximumDigits) {
decimalAt = MAX_COUNT - left;
// Don't copy trailing zeros. We are guaranteed that there is at
// least one non-zero digit, so we don't have to check lower bounds.
for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
;
right = MAX_COUNT - 1;
while (digits[right] == '0') {
--right;
}
count = right - left + 1;
System.arraycopy(digits, left, digits, 0, count);
}
if (maximumDigits > 0) round(maximumDigits, false, true);
if (maximumDigits > 0) {
roundInt(maximumDigits);
}
}

/**
Expand Down Expand Up @@ -692,13 +674,14 @@ final void set(boolean isNegative, BigInteger source, int maximumDigits) {
s.getChars(0, len, digits, 0);

decimalAt = len;
int right;
for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
;
int right = len - 1;
while (right >= 0 && digits[right] == '0') {
--right;
}
count = right + 1;

if (maximumDigits > 0) {
round(maximumDigits, false, true);
roundInt(maximumDigits);
}
}

Expand Down

1 comment on commit 96778dd

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.