8314169: Combine related RoundingMode logic in j.text.DigitList

Reviewed-by: naoto
This commit is contained in:
Justin Lu 2023-08-17 22:41:21 +00:00
parent 808bb1f7bc
commit 96778dd549

View file

@ -112,12 +112,21 @@ final class DigitList implements Cloneable {
* Return true if the represented number is zero. * Return true if the represented number is zero.
*/ */
boolean isZero() { 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') { if (digits[i] != '0') {
return false; return true;
} }
} }
return true; return false;
} }
/** /**
@ -190,9 +199,7 @@ final class DigitList implements Cloneable {
StringBuilder temp = getStringBuilder(); StringBuilder temp = getStringBuilder();
temp.append(digits, 0, count); temp.append(digits, 0, count);
for (int i = count; i < decimalAt; ++i) { temp.append("0".repeat(Math.max(0, decimalAt - count)));
temp.append('0');
}
return Long.parseLong(temp.toString()); return Long.parseLong(temp.toString());
} }
@ -392,6 +399,17 @@ final class DigitList implements Cloneable {
} }
/**
* 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. * Round the representation to the given number of digits.
* @param maximumDigits The maximum number of digits to be shown. * @param maximumDigits The maximum number of digits to be shown.
@ -408,25 +426,8 @@ final class DigitList implements Cloneable {
// Round up if appropriate. // Round up if appropriate.
if (maximumDigits >= 0 && maximumDigits < count) { if (maximumDigits >= 0 && maximumDigits < count) {
if (shouldRoundUp(maximumDigits, alreadyRounded, valueExactAsDecimal)) { if (shouldRoundUp(maximumDigits, alreadyRounded, valueExactAsDecimal)) {
// Rounding up involved incrementing digits from LSD to MSD. // Rounding can adjust the max digits
// In most cases this is simple, but in a worst case situation maximumDigits = roundUp(maximumDigits);
// (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
} }
count = maximumDigits; count = maximumDigits;
@ -508,94 +509,44 @@ final class DigitList implements Cloneable {
switch(roundingMode) { switch(roundingMode) {
case UP: case UP:
for (int i=maximumDigits; i<count; ++i) { return nonZeroAfterIndex(maximumDigits);
if (digits[i] != '0') {
return true;
}
}
break;
case DOWN: case DOWN:
break; break;
case CEILING: case CEILING:
for (int i=maximumDigits; i<count; ++i) { return nonZeroAfterIndex(maximumDigits) && !isNegative;
if (digits[i] != '0') {
return !isNegative;
}
}
break;
case FLOOR: case FLOOR:
for (int i=maximumDigits; i<count; ++i) { return nonZeroAfterIndex(maximumDigits) && isNegative;
if (digits[i] != '0') {
return isNegative;
}
}
break;
case HALF_UP: case HALF_UP:
case HALF_DOWN: case HALF_DOWN:
case HALF_EVEN:
// Above tie, round up for all cases
if (digits[maximumDigits] > '5') { if (digits[maximumDigits] > '5') {
// Value is above tie ==> must round up
return true; return true;
} else if (digits[maximumDigits] == '5') { // At tie, consider UP, DOWN, and EVEN logic
// Digit at rounding position is a '5'. Tie cases. } else if (digits[maximumDigits] == '5' ) {
if (maximumDigits != (count - 1)) { // Rounding position is the last index, there are 3 Cases.
// There are remaining digits. Above tie => must round up if (maximumDigits == (count - 1)) {
return true; // When exact, consider specific contract logic
} else {
// Digit at rounding position is the last one !
if (valueExactAsDecimal) { if (valueExactAsDecimal) {
// Exact binary representation. On the tie. return (roundingMode == RoundingMode.HALF_UP) ||
// Apply rounding given by roundingMode. (roundingMode == RoundingMode.HALF_EVEN
return roundingMode == RoundingMode.HALF_UP; && (maximumDigits > 0) && (digits[maximumDigits - 1] % 2 != 0));
// If already rounded, do not round again, otherwise round up
} else { } else {
// Not an exact binary representation.
// Digit sequence either rounded up or truncated.
// Round up only if it was truncated.
return !alreadyRounded; return !alreadyRounded;
} }
} // Rounding position is not the last index
} // If any further digits have a non-zero value, round up
// 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
if (digits[maximumDigits] > '5') {
return true;
} else if (digits[maximumDigits] == '5' ) {
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));
}
} else { } else {
// Rounds up if it gives a non null digit after '5' return nonZeroAfterIndex(maximumDigits+1);
for (int i=maximumDigits+1; i<count; ++i) {
if (digits[i] != '0')
return true;
}
} }
} }
// Below tie, do not round up for all cases
break; break;
case UNNECESSARY: case UNNECESSARY:
for (int i=maximumDigits; i<count; ++i) { if (nonZeroAfterIndex(maximumDigits)) {
if (digits[i] != '0') { throw new ArithmeticException(
throw new ArithmeticException(
"Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY"); "Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
}
} }
break; break;
default: default:
@ -605,6 +556,33 @@ final class DigitList implements Cloneable {
return false; 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 * Utility routine to set the value of the digit list from a long
*/ */
@ -649,12 +627,16 @@ final class DigitList implements Cloneable {
decimalAt = MAX_COUNT - left; decimalAt = MAX_COUNT - left;
// Don't copy trailing zeros. We are guaranteed that there is at // 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. // 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; count = right - left + 1;
System.arraycopy(digits, left, digits, 0, count); System.arraycopy(digits, left, digits, 0, count);
} }
if (maximumDigits > 0) round(maximumDigits, false, true); if (maximumDigits > 0) {
roundInt(maximumDigits);
}
} }
/** /**
@ -692,13 +674,14 @@ final class DigitList implements Cloneable {
s.getChars(0, len, digits, 0); s.getChars(0, len, digits, 0);
decimalAt = len; decimalAt = len;
int right; int right = len - 1;
for (right = len - 1; right >= 0 && digits[right] == '0'; --right) while (right >= 0 && digits[right] == '0') {
; --right;
}
count = right + 1; count = right + 1;
if (maximumDigits > 0) { if (maximumDigits > 0) {
round(maximumDigits, false, true); roundInt(maximumDigits);
} }
} }