Skip to content

Commit 96778dd

Browse files
author
Justin Lu
committedAug 17, 2023
8314169: Combine related RoundingMode logic in j.text.DigitList
Reviewed-by: naoto
1 parent 808bb1f commit 96778dd

File tree

1 file changed

+85
-102
lines changed

1 file changed

+85
-102
lines changed
 

‎src/java.base/share/classes/java/text/DigitList.java

+85-102
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,21 @@ final class DigitList implements Cloneable {
112112
* Return true if the represented number is zero.
113113
*/
114114
boolean isZero() {
115-
for (int i=0; i < count; ++i) {
115+
return !nonZeroAfterIndex(0);
116+
}
117+
118+
119+
/**
120+
* Return true if there exists a non-zero digit in the digit list
121+
* from the given index until the end.
122+
*/
123+
private boolean nonZeroAfterIndex(int index) {
124+
for (int i=index; i < count; ++i) {
116125
if (digits[i] != '0') {
117-
return false;
126+
return true;
118127
}
119128
}
120-
return true;
129+
return false;
121130
}
122131

123132
/**
@@ -190,9 +199,7 @@ public final long getLong() {
190199

191200
StringBuilder temp = getStringBuilder();
192201
temp.append(digits, 0, count);
193-
for (int i = count; i < decimalAt; ++i) {
194-
temp.append('0');
195-
}
202+
temp.append("0".repeat(Math.max(0, decimalAt - count)));
196203
return Long.parseLong(temp.toString());
197204
}
198205

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

393400
}
394401

402+
/**
403+
* Round the representation to the given number of digits.
404+
* @param maximumDigits The maximum number of digits to be shown.
405+
*
406+
* Upon return, count will be less than or equal to maximumDigits.
407+
*/
408+
private void roundInt(int maximumDigits) {
409+
// Integers do not need to worry about double rounding
410+
round(maximumDigits, false, true);
411+
}
412+
395413
/**
396414
* Round the representation to the given number of digits.
397415
* @param maximumDigits The maximum number of digits to be shown.
@@ -408,25 +426,8 @@ private final void round(int maximumDigits,
408426
// Round up if appropriate.
409427
if (maximumDigits >= 0 && maximumDigits < count) {
410428
if (shouldRoundUp(maximumDigits, alreadyRounded, valueExactAsDecimal)) {
411-
// Rounding up involved incrementing digits from LSD to MSD.
412-
// In most cases this is simple, but in a worst case situation
413-
// (9999..99) we have to adjust the decimalAt value.
414-
for (;;) {
415-
--maximumDigits;
416-
if (maximumDigits < 0) {
417-
// We have all 9's, so we increment to a single digit
418-
// of one and adjust the exponent.
419-
digits[0] = '1';
420-
++decimalAt;
421-
maximumDigits = 0; // Adjust the count
422-
break;
423-
}
424-
425-
++digits[maximumDigits];
426-
if (digits[maximumDigits] <= '9') break;
427-
// digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
428-
}
429-
++maximumDigits; // Increment for use as count
429+
// Rounding can adjust the max digits
430+
maximumDigits = roundUp(maximumDigits);
430431
}
431432
count = maximumDigits;
432433

@@ -508,94 +509,44 @@ private boolean shouldRoundUp(int maximumDigits,
508509

509510
switch(roundingMode) {
510511
case UP:
511-
for (int i=maximumDigits; i<count; ++i) {
512-
if (digits[i] != '0') {
513-
return true;
514-
}
515-
}
516-
break;
512+
return nonZeroAfterIndex(maximumDigits);
517513
case DOWN:
518514
break;
519515
case CEILING:
520-
for (int i=maximumDigits; i<count; ++i) {
521-
if (digits[i] != '0') {
522-
return !isNegative;
523-
}
524-
}
525-
break;
516+
return nonZeroAfterIndex(maximumDigits) && !isNegative;
526517
case FLOOR:
527-
for (int i=maximumDigits; i<count; ++i) {
528-
if (digits[i] != '0') {
529-
return isNegative;
530-
}
531-
}
532-
break;
518+
return nonZeroAfterIndex(maximumDigits) && isNegative;
533519
case HALF_UP:
534520
case HALF_DOWN:
535-
if (digits[maximumDigits] > '5') {
536-
// Value is above tie ==> must round up
537-
return true;
538-
} else if (digits[maximumDigits] == '5') {
539-
// Digit at rounding position is a '5'. Tie cases.
540-
if (maximumDigits != (count - 1)) {
541-
// There are remaining digits. Above tie => must round up
542-
return true;
543-
} else {
544-
// Digit at rounding position is the last one !
545-
if (valueExactAsDecimal) {
546-
// Exact binary representation. On the tie.
547-
// Apply rounding given by roundingMode.
548-
return roundingMode == RoundingMode.HALF_UP;
549-
} else {
550-
// Not an exact binary representation.
551-
// Digit sequence either rounded up or truncated.
552-
// Round up only if it was truncated.
553-
return !alreadyRounded;
554-
}
555-
}
556-
}
557-
// Digit at rounding position is < '5' ==> no round up.
558-
// Just let do the default, which is no round up (thus break).
559-
break;
560521
case HALF_EVEN:
561-
// Implement IEEE half-even rounding
522+
// Above tie, round up for all cases
562523
if (digits[maximumDigits] > '5') {
563524
return true;
525+
// At tie, consider UP, DOWN, and EVEN logic
564526
} else if (digits[maximumDigits] == '5' ) {
527+
// Rounding position is the last index, there are 3 Cases.
565528
if (maximumDigits == (count - 1)) {
566-
// the rounding position is exactly the last index :
567-
if (alreadyRounded)
568-
// If FloatingDecimal rounded up (value was below tie),
569-
// then we should not round up again.
570-
return false;
571-
572-
if (!valueExactAsDecimal)
573-
// Otherwise if the digits don't represent exact value,
574-
// value was above tie and FloatingDecimal truncated
575-
// digits to tie. We must round up.
576-
return true;
577-
else {
578-
// This is an exact tie value, and FloatingDecimal
579-
// provided all of the exact digits. We thus apply
580-
// HALF_EVEN rounding rule.
581-
return ((maximumDigits > 0) &&
582-
(digits[maximumDigits-1] % 2 != 0));
529+
// When exact, consider specific contract logic
530+
if (valueExactAsDecimal) {
531+
return (roundingMode == RoundingMode.HALF_UP) ||
532+
(roundingMode == RoundingMode.HALF_EVEN
533+
&& (maximumDigits > 0) && (digits[maximumDigits - 1] % 2 != 0));
534+
// If already rounded, do not round again, otherwise round up
535+
} else {
536+
return !alreadyRounded;
583537
}
538+
// Rounding position is not the last index
539+
// If any further digits have a non-zero value, round up
584540
} else {
585-
// Rounds up if it gives a non null digit after '5'
586-
for (int i=maximumDigits+1; i<count; ++i) {
587-
if (digits[i] != '0')
588-
return true;
589-
}
541+
return nonZeroAfterIndex(maximumDigits+1);
590542
}
591543
}
544+
// Below tie, do not round up for all cases
592545
break;
593546
case UNNECESSARY:
594-
for (int i=maximumDigits; i<count; ++i) {
595-
if (digits[i] != '0') {
596-
throw new ArithmeticException(
547+
if (nonZeroAfterIndex(maximumDigits)) {
548+
throw new ArithmeticException(
597549
"Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
598-
}
599550
}
600551
break;
601552
default:
@@ -605,6 +556,33 @@ private boolean shouldRoundUp(int maximumDigits,
605556
return false;
606557
}
607558

559+
/**
560+
* Round the digit list up numerically.
561+
* This involves incrementing digits from the LSD to the MSD.
562+
* @param maximumDigits The maximum number of digits to be shown.
563+
* @return The new maximum digits after rounding.
564+
*/
565+
private int roundUp(int maximumDigits) {
566+
do {
567+
--maximumDigits;
568+
/*
569+
* We have exhausted the max digits while attempting to round up
570+
* from the LSD to the MSD. This implies a value of all 9's. As such,
571+
* adjust representation to a single digit of one and increment the exponent.
572+
*/
573+
if (maximumDigits < 0) {
574+
digits[0] = '1';
575+
++decimalAt;
576+
maximumDigits = 0; // Adjust the count
577+
break;
578+
}
579+
++digits[maximumDigits];
580+
}
581+
while (digits[maximumDigits] > '9');
582+
583+
return ++maximumDigits; // Increment for use as count
584+
}
585+
608586
/**
609587
* Utility routine to set the value of the digit list from a long
610588
*/
@@ -649,12 +627,16 @@ final void set(boolean isNegative, long source, int maximumDigits) {
649627
decimalAt = MAX_COUNT - left;
650628
// Don't copy trailing zeros. We are guaranteed that there is at
651629
// least one non-zero digit, so we don't have to check lower bounds.
652-
for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
653-
;
630+
right = MAX_COUNT - 1;
631+
while (digits[right] == '0') {
632+
--right;
633+
}
654634
count = right - left + 1;
655635
System.arraycopy(digits, left, digits, 0, count);
656636
}
657-
if (maximumDigits > 0) round(maximumDigits, false, true);
637+
if (maximumDigits > 0) {
638+
roundInt(maximumDigits);
639+
}
658640
}
659641

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

694676
decimalAt = len;
695-
int right;
696-
for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
697-
;
677+
int right = len - 1;
678+
while (right >= 0 && digits[right] == '0') {
679+
--right;
680+
}
698681
count = right + 1;
699682

700683
if (maximumDigits > 0) {
701-
round(maximumDigits, false, true);
684+
roundInt(maximumDigits);
702685
}
703686
}
704687

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Aug 17, 2023

@openjdk-notifier[bot]
Please sign in to comment.