ICU-20494 Fixes to very large magnitude exponents in number parsing.
- Do not depend on ArithmeticException string in ICU4J.
- Return correct string in ICU4C.
- Fix related issue in applyMaxInteger.
diff --git a/icu4c/source/i18n/fmtable.cpp b/icu4c/source/i18n/fmtable.cpp
index 82456f9..d813424 100644
--- a/icu4c/source/i18n/fmtable.cpp
+++ b/icu4c/source/i18n/fmtable.cpp
@@ -732,7 +732,11 @@
// Older ICUs called uprv_decNumberToString here, which is not exactly the same as
// DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
// not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?).
- if (fDecimalQuantity->isZero()) {
+ if (fDecimalQuantity->isInfinite()) {
+ fDecimalStr->append("Infinity", status);
+ } else if (fDecimalQuantity->isNaN()) {
+ fDecimalStr->append("NaN", status);
+ } else if (fDecimalQuantity->isZero()) {
fDecimalStr->append("0", -1, status);
} else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
(fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp
index 2fde292..d899c27 100644
--- a/icu4c/source/i18n/number_decimalquantity.cpp
+++ b/icu4c/source/i18n/number_decimalquantity.cpp
@@ -160,6 +160,11 @@
return;
}
+ if (maxInt <= scale) {
+ setBcdToZero();
+ return;
+ }
+
int32_t magnitude = getMagnitude();
if (maxInt <= magnitude) {
popFromLeft(magnitude - maxInt + 1);
@@ -983,6 +988,7 @@
}
void DecimalQuantity::popFromLeft(int32_t numDigits) {
+ U_ASSERT(numDigits <= precision);
if (usingBytes) {
int i = precision - 1;
for (; i >= precision - numDigits; i--) {
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 902d27f..68a0e7b 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -1657,6 +1657,31 @@
u"00.08765",
u"00.008765",
u"00");
+
+ assertFormatSingle(
+ u"Integer Width Remove All A",
+ u"integer-width/00",
+ NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
+ "en",
+ 2500,
+ u"00");
+
+ assertFormatSingle(
+ u"Integer Width Remove All B",
+ u"integer-width/00",
+ NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
+ "en",
+ 25000,
+ u"00");
+
+ assertFormatSingle(
+ u"Integer Width Remove All B, Bytes Mode",
+ u"integer-width/00",
+ NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
+ "en",
+ // Note: this double produces all 17 significant digits
+ 10000000000000002000.0,
+ u"00");
}
void NumberFormatterApiTest::symbols() {
diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp
index 7dd66e5..5c0e849 100644
--- a/icu4c/source/test/intltest/numfmtst.cpp
+++ b/icu4c/source/test/intltest/numfmtst.cpp
@@ -9443,6 +9443,32 @@
assertEquals(u"Should not overflow",
u"3E-2147483648",
{sp.data(), sp.length(), US_INV});
+
+ // Test largest parseable exponent
+ result = Formattable();
+ nf->parse(u"9876e2147483643", result, status);
+ sp = result.getDecimalNumber(status);
+ assertEquals(u"Should not overflow",
+ u"9.876E+2147483646",
+ {sp.data(), sp.length(), US_INV});
+
+ // Test max value as well
+ const char16_t* infinityInputs[] = {
+ u"9876e2147483644",
+ u"9876e2147483645",
+ u"9876e2147483646",
+ u"9876e2147483647",
+ u"9876e2147483648",
+ u"9876e2147483649",
+ };
+ for (const auto& input : infinityInputs) {
+ result = Formattable();
+ nf->parse(input, result, status);
+ sp = result.getDecimalNumber(status);
+ assertEquals(UnicodeString("Should become Infinity: ") + input,
+ u"Infinity",
+ {sp.data(), sp.length(), US_INV});
+ }
}
void NumberFormatTest::Test13840_ParseLongStringCrash() {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
index 8743493..58a04c8 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
@@ -141,6 +141,11 @@
return;
}
+ if (maxInt <= scale) {
+ setBcdToZero();
+ return;
+ }
+
int magnitude = getMagnitude();
if (maxInt <= magnitude) {
popFromLeft(magnitude - maxInt + 1);
@@ -205,6 +210,8 @@
if (precision != 0) {
scale = Utility.addExact(scale, delta);
origDelta = Utility.addExact(origDelta, delta);
+ // Make sure that precision + scale won't overflow, either
+ Utility.addExact(scale, precision);
}
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java
index 791538c..47f0d97 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java
@@ -156,6 +156,7 @@
@Override
protected void popFromLeft(int numDigits) {
+ assert numDigits <= precision;
if (usingBytes) {
int i = precision - 1;
for (; i >= precision - numDigits; i--) {
@@ -252,17 +253,16 @@
tempLong = tempLong * 10 + getDigitPos(shift);
}
BigDecimal result = BigDecimal.valueOf(tempLong);
- try {
+ // Test that the new scale fits inside the BigDecimal
+ long newScale = result.scale() + scale;
+ if (newScale <= Integer.MIN_VALUE) {
+ result = BigDecimal.ZERO;
+ } else {
result = result.scaleByPowerOfTen(scale);
- } catch (ArithmeticException e) {
- if (e.getMessage().contains("Underflow")) {
- result = BigDecimal.ZERO;
- } else {
- throw e;
- }
}
- if (isNegative())
+ if (isNegative()) {
result = result.negate();
+ }
return result;
}
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
index 0c76bd0..f6b5752 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
@@ -6511,6 +6511,32 @@
result = nf.parse(".0003e-2147483644");
assertEquals("Should not overflow",
"0", result.toString());
+
+ // Test largest parseable exponent
+ // This is limited by ICU's BigDecimal implementation
+ result = nf.parse("1e999999999");
+ assertEquals("Should not overflow",
+ "1E+999999999", result.toString());
+
+ // Test max value as well
+ String[] infinityInputs = {
+ "9876e1000000000",
+ "9876e2147483640",
+ "9876e2147483641",
+ "9876e2147483642",
+ "9876e2147483643",
+ "9876e2147483644",
+ "9876e2147483645",
+ "9876e2147483646",
+ "9876e2147483647",
+ "9876e2147483648",
+ "9876e2147483649",
+ };
+ for (String input : infinityInputs) {
+ result = nf.parse(input);
+ assertEquals("Should become Infinity: " + input,
+ "Infinity", result.toString());
+ }
}
@Test
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
index 9421097..e0ad131 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -1591,6 +1591,31 @@
"00.08765",
"00.008765",
"00");
+
+ assertFormatSingle(
+ "Integer Width Remove All A",
+ "integer-width/00",
+ NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
+ ULocale.ENGLISH,
+ 2500,
+ "00");
+
+ assertFormatSingle(
+ "Integer Width Remove All B",
+ "integer-width/00",
+ NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
+ ULocale.ENGLISH,
+ 25000,
+ "00");
+
+ assertFormatSingle(
+ "Integer Width Remove All B, Bytes Mode",
+ "integer-width/00",
+ NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
+ ULocale.ENGLISH,
+ // Note: this double produces all 17 significant digits
+ 10000000000000002000.0,
+ "00");
}
@Test