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.
This commit is contained in:
parent
b5ad35dda8
commit
e318c0c374
@ -732,7 +732,11 @@ CharString *Formattable::internalGetCharString(UErrorCode &status) {
|
|||||||
// Older ICUs called uprv_decNumberToString here, which is not exactly the same as
|
// Older ICUs called uprv_decNumberToString here, which is not exactly the same as
|
||||||
// DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
|
// 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?).
|
// 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);
|
fDecimalStr->append("0", -1, status);
|
||||||
} else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
|
} else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
|
||||||
(fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
|
(fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
|
||||||
|
@ -160,6 +160,11 @@ void DecimalQuantity::applyMaxInteger(int32_t maxInt) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxInt <= scale) {
|
||||||
|
setBcdToZero();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t magnitude = getMagnitude();
|
int32_t magnitude = getMagnitude();
|
||||||
if (maxInt <= magnitude) {
|
if (maxInt <= magnitude) {
|
||||||
popFromLeft(magnitude - maxInt + 1);
|
popFromLeft(magnitude - maxInt + 1);
|
||||||
@ -983,6 +988,7 @@ void DecimalQuantity::shiftRight(int32_t numDigits) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DecimalQuantity::popFromLeft(int32_t numDigits) {
|
void DecimalQuantity::popFromLeft(int32_t numDigits) {
|
||||||
|
U_ASSERT(numDigits <= precision);
|
||||||
if (usingBytes) {
|
if (usingBytes) {
|
||||||
int i = precision - 1;
|
int i = precision - 1;
|
||||||
for (; i >= precision - numDigits; i--) {
|
for (; i >= precision - numDigits; i--) {
|
||||||
|
@ -1657,6 +1657,31 @@ void NumberFormatterApiTest::integerWidth() {
|
|||||||
u"00.08765",
|
u"00.08765",
|
||||||
u"00.008765",
|
u"00.008765",
|
||||||
u"00");
|
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() {
|
void NumberFormatterApiTest::symbols() {
|
||||||
|
@ -9443,6 +9443,32 @@ void NumberFormatTest::Test20037_ScientificIntegerOverflow() {
|
|||||||
assertEquals(u"Should not overflow",
|
assertEquals(u"Should not overflow",
|
||||||
u"3E-2147483648",
|
u"3E-2147483648",
|
||||||
{sp.data(), sp.length(), US_INV});
|
{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() {
|
void NumberFormatTest::Test13840_ParseLongStringCrash() {
|
||||||
|
@ -141,6 +141,11 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxInt <= scale) {
|
||||||
|
setBcdToZero();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int magnitude = getMagnitude();
|
int magnitude = getMagnitude();
|
||||||
if (maxInt <= magnitude) {
|
if (maxInt <= magnitude) {
|
||||||
popFromLeft(magnitude - maxInt + 1);
|
popFromLeft(magnitude - maxInt + 1);
|
||||||
@ -205,6 +210,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||||||
if (precision != 0) {
|
if (precision != 0) {
|
||||||
scale = Utility.addExact(scale, delta);
|
scale = Utility.addExact(scale, delta);
|
||||||
origDelta = Utility.addExact(origDelta, delta);
|
origDelta = Utility.addExact(origDelta, delta);
|
||||||
|
// Make sure that precision + scale won't overflow, either
|
||||||
|
Utility.addExact(scale, precision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +156,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void popFromLeft(int numDigits) {
|
protected void popFromLeft(int numDigits) {
|
||||||
|
assert numDigits <= precision;
|
||||||
if (usingBytes) {
|
if (usingBytes) {
|
||||||
int i = precision - 1;
|
int i = precision - 1;
|
||||||
for (; i >= precision - numDigits; i--) {
|
for (; i >= precision - numDigits; i--) {
|
||||||
@ -252,17 +253,16 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
|||||||
tempLong = tempLong * 10 + getDigitPos(shift);
|
tempLong = tempLong * 10 + getDigitPos(shift);
|
||||||
}
|
}
|
||||||
BigDecimal result = BigDecimal.valueOf(tempLong);
|
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);
|
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();
|
result = result.negate();
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6511,6 +6511,32 @@ public class NumberFormatTest extends TestFmwk {
|
|||||||
result = nf.parse(".0003e-2147483644");
|
result = nf.parse(".0003e-2147483644");
|
||||||
assertEquals("Should not overflow",
|
assertEquals("Should not overflow",
|
||||||
"0", result.toString());
|
"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
|
@Test
|
||||||
|
@ -1591,6 +1591,31 @@ public class NumberFormatterApiTest {
|
|||||||
"00.08765",
|
"00.08765",
|
||||||
"00.008765",
|
"00.008765",
|
||||||
"00");
|
"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
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user