mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8338690: CompactNumberInstance.format incorrectly formats some numbers (few vs many)
Reviewed-by: joehw, rriggs, jlu
This commit is contained in:
parent
1ff5f8d65c
commit
fa4ff78bd4
3 changed files with 73 additions and 48 deletions
|
@ -652,16 +652,16 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
double val = getNumberValue(number, divisor);
|
double val = getNumberValue(number, divisor);
|
||||||
if (checkIncrement(val, compactDataIndex, divisor)) {
|
if (checkIncrement(val, compactDataIndex, divisor)) {
|
||||||
divisor = (Long) divisors.get(++compactDataIndex);
|
divisor = (Long) divisors.get(++compactDataIndex);
|
||||||
val = getNumberValue(number, divisor);
|
|
||||||
}
|
}
|
||||||
|
roundedNumber = roundedNumber / divisor;
|
||||||
|
decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits());
|
||||||
|
val = decimalFormat.getDigitList().getDouble();
|
||||||
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
||||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
||||||
|
|
||||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||||
appendPrefix(result, prefix, delegate);
|
appendPrefix(result, prefix, delegate);
|
||||||
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
||||||
roundedNumber = roundedNumber / divisor;
|
|
||||||
decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits());
|
|
||||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||||
|
@ -734,31 +734,28 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
double val = getNumberValue(number, divisor);
|
double val = getNumberValue(number, divisor);
|
||||||
if (checkIncrement(val, compactDataIndex, divisor)) {
|
if (checkIncrement(val, compactDataIndex, divisor)) {
|
||||||
divisor = (Long) divisors.get(++compactDataIndex);
|
divisor = (Long) divisors.get(++compactDataIndex);
|
||||||
val = getNumberValue(number, divisor);
|
|
||||||
}
|
}
|
||||||
|
var noFraction = number % divisor == 0;
|
||||||
|
if (noFraction) {
|
||||||
|
number = number / divisor;
|
||||||
|
decimalFormat.setDigitList(number, isNegative, 0);
|
||||||
|
} else {
|
||||||
|
// To avoid truncation of fractional part store
|
||||||
|
// the value in double and follow double path instead of
|
||||||
|
// long path
|
||||||
|
double dNumber = (double) number / divisor;
|
||||||
|
decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits());
|
||||||
|
}
|
||||||
|
val = decimalFormat.getDigitList().getDouble();
|
||||||
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
||||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
||||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||||
appendPrefix(result, prefix, delegate);
|
appendPrefix(result, prefix, delegate);
|
||||||
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
||||||
if ((number % divisor == 0)) {
|
decimalFormat.subformatNumber(result, delegate,
|
||||||
number = number / divisor;
|
isNegative, noFraction, getMaximumIntegerDigits(),
|
||||||
decimalFormat.setDigitList(number, isNegative, 0);
|
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||||
decimalFormat.subformatNumber(result, delegate,
|
getMinimumFractionDigits());
|
||||||
isNegative, true, getMaximumIntegerDigits(),
|
|
||||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
|
||||||
getMinimumFractionDigits());
|
|
||||||
} else {
|
|
||||||
// To avoid truncation of fractional part store
|
|
||||||
// the value in double and follow double path instead of
|
|
||||||
// long path
|
|
||||||
double dNumber = (double) number / divisor;
|
|
||||||
decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits());
|
|
||||||
decimalFormat.subformatNumber(result, delegate,
|
|
||||||
isNegative, false, getMaximumIntegerDigits(),
|
|
||||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
|
||||||
getMinimumFractionDigits());
|
|
||||||
}
|
|
||||||
appendSuffix(result, suffix, delegate);
|
appendSuffix(result, suffix, delegate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -833,15 +830,15 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
double val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
double val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
||||||
if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) {
|
if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) {
|
||||||
divisor = divisors.get(++compactDataIndex);
|
divisor = divisors.get(++compactDataIndex);
|
||||||
val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
|
||||||
}
|
}
|
||||||
|
number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||||
|
decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits());
|
||||||
|
val = decimalFormat.getDigitList().getDouble();
|
||||||
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
||||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
||||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||||
appendPrefix(result, prefix, delegate);
|
appendPrefix(result, prefix, delegate);
|
||||||
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
||||||
number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
|
||||||
decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits());
|
|
||||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||||
|
@ -904,34 +901,30 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||||
double val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
double val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
||||||
if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) {
|
if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) {
|
||||||
divisor = divisors.get(++compactDataIndex);
|
divisor = divisors.get(++compactDataIndex);
|
||||||
val = getNumberValue(number.doubleValue(), divisor.doubleValue());
|
|
||||||
}
|
}
|
||||||
|
var noFraction = number.mod(new BigInteger(divisor.toString()))
|
||||||
|
.compareTo(BigInteger.ZERO) == 0;
|
||||||
|
if (noFraction) {
|
||||||
|
number = number.divide(new BigInteger(divisor.toString()));
|
||||||
|
decimalFormat.setDigitList(number, isNegative, 0);
|
||||||
|
} else {
|
||||||
|
// To avoid truncation of fractional part store the value in
|
||||||
|
// BigDecimal and follow BigDecimal path instead of
|
||||||
|
// BigInteger path
|
||||||
|
BigDecimal nDecimal = new BigDecimal(number)
|
||||||
|
.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||||
|
decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits());
|
||||||
|
}
|
||||||
|
val = decimalFormat.getDigitList().getDouble();
|
||||||
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
String prefix = getAffix(false, true, isNegative, compactDataIndex, val);
|
||||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
String suffix = getAffix(false, false, isNegative, compactDataIndex, val);
|
||||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||||
appendPrefix(result, prefix, delegate);
|
appendPrefix(result, prefix, delegate);
|
||||||
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) {
|
||||||
if (number.mod(new BigInteger(divisor.toString()))
|
decimalFormat.subformatNumber(result, delegate,
|
||||||
.compareTo(BigInteger.ZERO) == 0) {
|
isNegative, noFraction, getMaximumIntegerDigits(),
|
||||||
number = number.divide(new BigInteger(divisor.toString()));
|
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||||
|
getMinimumFractionDigits());
|
||||||
decimalFormat.setDigitList(number, isNegative, 0);
|
|
||||||
decimalFormat.subformatNumber(result, delegate,
|
|
||||||
isNegative, true, getMaximumIntegerDigits(),
|
|
||||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
|
||||||
getMinimumFractionDigits());
|
|
||||||
} else {
|
|
||||||
// To avoid truncation of fractional part store the value in
|
|
||||||
// BigDecimal and follow BigDecimal path instead of
|
|
||||||
// BigInteger path
|
|
||||||
BigDecimal nDecimal = new BigDecimal(number)
|
|
||||||
.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
|
||||||
decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits());
|
|
||||||
decimalFormat.subformatNumber(result, delegate,
|
|
||||||
isNegative, false, getMaximumIntegerDigits(),
|
|
||||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
|
||||||
getMinimumFractionDigits());
|
|
||||||
}
|
|
||||||
appendSuffix(result, suffix, delegate);
|
appendSuffix(result, suffix, delegate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1799,6 +1799,14 @@ public class DecimalFormat extends NumberFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the {@code DigitList} used by this {@code DecimalFormat} instance}
|
||||||
|
* Declared as package-private, intended for {@code CompactNumberFormat}.
|
||||||
|
*/
|
||||||
|
DigitList getDigitList() {
|
||||||
|
return digitList;
|
||||||
|
}
|
||||||
|
|
||||||
// ======== End fast-path formatting logic for double =========================
|
// ======== End fast-path formatting logic for double =========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8177552 8217721 8222756 8295372 8306116 8319990
|
* @bug 8177552 8217721 8222756 8295372 8306116 8319990 8338690
|
||||||
* @summary Checks the functioning of compact number format
|
* @summary Checks the functioning of compact number format
|
||||||
* @modules jdk.localedata
|
* @modules jdk.localedata
|
||||||
* @run testng/othervm TestCompactNumber
|
* @run testng/othervm TestCompactNumber
|
||||||
|
@ -60,6 +60,9 @@ public class TestCompactNumber {
|
||||||
private static final NumberFormat FORMAT_IT_SHORT = NumberFormat
|
private static final NumberFormat FORMAT_IT_SHORT = NumberFormat
|
||||||
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.SHORT);
|
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.SHORT);
|
||||||
|
|
||||||
|
private static final NumberFormat FORMAT_IT_LONG = NumberFormat
|
||||||
|
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG);
|
||||||
|
|
||||||
private static final NumberFormat FORMAT_CA_LONG = NumberFormat
|
private static final NumberFormat FORMAT_CA_LONG = NumberFormat
|
||||||
.getCompactNumberInstance(Locale.of("ca"), NumberFormat.Style.LONG);
|
.getCompactNumberInstance(Locale.of("ca"), NumberFormat.Style.LONG);
|
||||||
|
|
||||||
|
@ -89,6 +92,13 @@ public class TestCompactNumber {
|
||||||
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG);
|
.getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG);
|
||||||
private static final NumberFormat FORMAT_PT_LONG_FD4 = NumberFormat
|
private static final NumberFormat FORMAT_PT_LONG_FD4 = NumberFormat
|
||||||
.getCompactNumberInstance(Locale.of("pt"), NumberFormat.Style.LONG);
|
.getCompactNumberInstance(Locale.of("pt"), NumberFormat.Style.LONG);
|
||||||
|
|
||||||
|
private static final NumberFormat FORMAT_PL_LONG = NumberFormat
|
||||||
|
.getCompactNumberInstance(Locale.of("pl"), NumberFormat.Style.LONG);
|
||||||
|
|
||||||
|
private static final NumberFormat FORMAT_FR_LONG = NumberFormat
|
||||||
|
.getCompactNumberInstance(Locale.FRENCH, NumberFormat.Style.LONG);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
FORMAT_ES_LONG_FD1.setMaximumFractionDigits(1);
|
FORMAT_ES_LONG_FD1.setMaximumFractionDigits(1);
|
||||||
FORMAT_DE_LONG_FD2.setMaximumFractionDigits(2);
|
FORMAT_DE_LONG_FD2.setMaximumFractionDigits(2);
|
||||||
|
@ -359,6 +369,12 @@ public class TestCompactNumber {
|
||||||
{FORMAT_DE_LONG_FD2, 1_234_500, "1,23 Millionen"},
|
{FORMAT_DE_LONG_FD2, 1_234_500, "1,23 Millionen"},
|
||||||
{FORMAT_IT_LONG_FD3, 1_234_500, "1,234 milioni"},
|
{FORMAT_IT_LONG_FD3, 1_234_500, "1,234 milioni"},
|
||||||
{FORMAT_PT_LONG_FD4, 1_234_500, "1,2345 milh\u00f5es"},
|
{FORMAT_PT_LONG_FD4, 1_234_500, "1,2345 milh\u00f5es"},
|
||||||
|
|
||||||
|
// 8338690
|
||||||
|
{FORMAT_PL_LONG, 5_000, "5 tysi\u0119cy"},
|
||||||
|
{FORMAT_PL_LONG, 4_949, "5 tysi\u0119cy"},
|
||||||
|
{FORMAT_FR_LONG, 1_949, "2 mille"},
|
||||||
|
{FORMAT_IT_LONG, 1_949, "2 mila"},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +482,10 @@ public class TestCompactNumber {
|
||||||
{FORMAT_DE_LONG_FD2, "1,23 Millionen", 1_230_000L, Long.class},
|
{FORMAT_DE_LONG_FD2, "1,23 Millionen", 1_230_000L, Long.class},
|
||||||
{FORMAT_IT_LONG_FD3, "1,234 milioni", 1_234_000L, Long.class},
|
{FORMAT_IT_LONG_FD3, "1,234 milioni", 1_234_000L, Long.class},
|
||||||
{FORMAT_PT_LONG_FD4, "1,2345 milh\u00f5es", 1_234_500L, Long.class},
|
{FORMAT_PT_LONG_FD4, "1,2345 milh\u00f5es", 1_234_500L, Long.class},
|
||||||
|
// 8338690
|
||||||
|
{FORMAT_PL_LONG, "5 tysi\u0119cy", 5_000L, Long.class},
|
||||||
|
{FORMAT_FR_LONG, "2 mille", 2_000L, Long.class},
|
||||||
|
{FORMAT_IT_LONG, "2 mila", 2_000L, Long.class},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,6 +534,10 @@ public class TestCompactNumber {
|
||||||
{FORMAT_SL_LONG, "5 milijon", 5L},
|
{FORMAT_SL_LONG, "5 milijon", 5L},
|
||||||
{FORMAT_SL_LONG, "5 milijona", 5L},
|
{FORMAT_SL_LONG, "5 milijona", 5L},
|
||||||
{FORMAT_SL_LONG, "5 milijone", 5L},
|
{FORMAT_SL_LONG, "5 milijone", 5L},
|
||||||
|
// 8338690
|
||||||
|
{FORMAT_PL_LONG, "5 tysiące", 5L},
|
||||||
|
{FORMAT_FR_LONG, "2 millier", 2L},
|
||||||
|
{FORMAT_IT_LONG, "2 mille", 2L},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue