mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 08:53:20 +00:00
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
8 changed files with 128 additions and 9 deletions
|
@ -732,7 +732,11 @@ CharString *Formattable::internalGetCharString(UErrorCode &status) {
|
|||
// 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)) {
|
||||
|
|
|
@ -160,6 +160,11 @@ void DecimalQuantity::applyMaxInteger(int32_t maxInt) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (maxInt <= scale) {
|
||||
setBcdToZero();
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t magnitude = getMagnitude();
|
||||
if (maxInt <= magnitude) {
|
||||
popFromLeft(magnitude - maxInt + 1);
|
||||
|
@ -983,6 +988,7 @@ void DecimalQuantity::shiftRight(int32_t numDigits) {
|
|||
}
|
||||
|
||||
void DecimalQuantity::popFromLeft(int32_t numDigits) {
|
||||
U_ASSERT(numDigits <= precision);
|
||||
if (usingBytes) {
|
||||
int i = precision - 1;
|
||||
for (; i >= precision - numDigits; i--) {
|
||||
|
|
|
@ -1657,6 +1657,31 @@ void NumberFormatterApiTest::integerWidth() {
|
|||
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() {
|
||||
|
|
|
@ -9443,6 +9443,32 @@ void NumberFormatTest::Test20037_ScientificIntegerOverflow() {
|
|||
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() {
|
||||
|
|
|
@ -141,6 +141,11 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
return;
|
||||
}
|
||||
|
||||
if (maxInt <= scale) {
|
||||
setBcdToZero();
|
||||
return;
|
||||
}
|
||||
|
||||
int magnitude = getMagnitude();
|
||||
if (maxInt <= magnitude) {
|
||||
popFromLeft(magnitude - maxInt + 1);
|
||||
|
@ -205,6 +210,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,6 +156,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
|||
|
||||
@Override
|
||||
protected void popFromLeft(int numDigits) {
|
||||
assert numDigits <= precision;
|
||||
if (usingBytes) {
|
||||
int i = precision - 1;
|
||||
for (; i >= precision - numDigits; i--) {
|
||||
|
@ -252,17 +253,16 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6511,6 +6511,32 @@ public class NumberFormatTest extends TestFmwk {
|
|||
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
|
||||
|
|
|
@ -1591,6 +1591,31 @@ public class NumberFormatterApiTest {
|
|||
"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
|
||||
|
|
Loading…
Add table
Reference in a new issue