ICU-13634 A variety of mostly minor changes to fix assorted unit test failures in ICU4C plus a few in ICU4J.

X-SVN-Rev: 41236
This commit is contained in:
Shane Carr 2018-04-17 08:05:20 +00:00
parent d6c6fa0404
commit 12b34e7c9e
27 changed files with 170 additions and 125 deletions

View file

@ -512,7 +512,7 @@ void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
// TODO: Do we need to check for fProperties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
if (result.success()) {
parsePosition.setIndex(result.charEnd);
result.populateFormattable(output);
result.populateFormattable(output, fParser->getParseFlags());
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd);
}
@ -533,7 +533,7 @@ CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePos
if (result.success()) {
parsePosition.setIndex(result.charEnd);
Formattable formattable;
result.populateFormattable(formattable);
result.populateFormattable(formattable, fParser->getParseFlags());
return new CurrencyAmount(formattable, result.currencyCode, status);
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd);

View file

@ -462,12 +462,12 @@ Formattable::getInt64(UErrorCode& status) const
status = U_INVALID_FORMAT_ERROR;
return U_INT64_MIN;
} else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) {
if (fDecimalQuantity->fitsInLong()) {
if (fDecimalQuantity->fitsInLong(true)) {
return fDecimalQuantity->toLong();
} else if (fDecimalQuantity->isNegative()) {
return U_INT64_MIN;
} else {
return U_INT64_MAX;
// Unexpected
status = U_INVALID_FORMAT_ERROR;
return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX;
}
} else {
return (int64_t)fValue.fDouble;

View file

@ -173,9 +173,11 @@ void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode ro
roundToInfinity();
double temp = toDouble();
temp /= roundingIncrement;
setToDouble(temp);
roundToMagnitude(0, roundingMode, status);
temp = toDouble();
// Use another DecimalQuantity to perform the actual rounding...
DecimalQuantity dq;
dq.setToDouble(temp);
dq.roundToMagnitude(0, roundingMode, status);
temp = dq.toDouble();
temp *= roundingIncrement;
setToDouble(temp);
// Since we reset the value to a double, we need to specify the rounding boundary
@ -498,8 +500,7 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
// NOTE: Call sites should be guarded by fitsInLong(), like this:
// if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
// Fallback behavior upon truncateIfOverflow is to truncate at 17 digits.
U_ASSERT(truncateIfOverflow || fitsInLong());
int64_t result = 0L;
uint64_t result = 0L;
int32_t upperMagnitude = std::min(scale + precision, lOptPos) - 1;
if (truncateIfOverflow) {
upperMagnitude = std::min(upperMagnitude, 17);
@ -508,7 +509,7 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
result = result * 10 + getDigitPos(magnitude - scale);
}
if (isNegative()) {
result = -result;
return -result;
}
return result;
}
@ -532,11 +533,11 @@ uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
return result;
}
bool DecimalQuantity::fitsInLong() const {
bool DecimalQuantity::fitsInLong(bool ignoreFraction) const {
if (isZero()) {
return true;
}
if (scale < 0) {
if (scale < 0 && !ignoreFraction) {
return false;
}
int magnitude = getMagnitude();

View file

@ -150,8 +150,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
/**
* Returns whether or not a Long can fully represent the value stored in this DecimalQuantity.
* @param ignoreFraction if true, silently ignore digits after the decimal place.
*/
bool fitsInLong() const;
bool fitsInLong(bool ignoreFraction = false) const;
/** @return The value contained in this {@link DecimalQuantity} approximated as a double. */
double toDouble() const;

View file

@ -25,7 +25,7 @@ void DecimalFormatProperties::clear() {
exponentSignAlwaysShown = false;
formatWidth = -1;
groupingSize = -1;
groupingUsed = false;
groupingUsed = true;
magnitudeMultiplier = 0;
maximumFractionDigits = -1;
maximumIntegerDigits = -1;

View file

@ -119,7 +119,7 @@ struct U_I18N_API DecimalFormatProperties {
bool parseIntegerOnly;
NullableValue<ParseMode> parseMode;
bool parseNoExponent;
bool parseToBigDecimal;
bool parseToBigDecimal; // TODO: Not needed in ICU4C?
UNumberFormatAttributeValue parseAllInput; // ICU4C-only
//PluralRules pluralRules;
UnicodeString positivePrefix;

View file

@ -254,6 +254,12 @@ bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, int8_t
break;
}
// Back up if there was a trailing grouping separator
if (backupOffset != -1) {
segment.setOffset(backupOffset);
hasPartialPrefix = true; // redundant with `groupingOverlap == segment.length()`
}
// Check the final grouping for validity
if (requireGroupingMatch && !seenDecimal && seenGrouping && afterFirstGrouping &&
groupedDigitCount != grouping1) {

View file

@ -232,6 +232,10 @@ void NumberParserImpl::freeze() {
fFrozen = true;
}
parse_flags_t NumberParserImpl::getParseFlags() const {
return fParseFlags;
}
void NumberParserImpl::parse(const UnicodeString& input, bool greedy, ParsedNumber& result,
UErrorCode& status) const {
return parse(input, 0, greedy, result, status);

View file

@ -42,6 +42,8 @@ class NumberParserImpl : public MutableMatcherCollection {
void freeze();
parse_flags_t getParseFlags() const;
void parse(const UnicodeString& input, bool greedy, ParsedNumber& result, UErrorCode& status) const;
void parse(const UnicodeString& input, int32_t start, bool greedy, ParsedNumber& result,

View file

@ -78,9 +78,10 @@ double ParsedNumber::getDouble() const {
}
}
void ParsedNumber::populateFormattable(Formattable& output) const {
void ParsedNumber::populateFormattable(Formattable& output, parse_flags_t parseFlags) const {
bool sawNaN = 0 != (flags & FLAG_NAN);
bool sawInfinity = 0 != (flags & FLAG_INFINITY);
bool integerOnly = 0 != (parseFlags & PARSE_FLAG_INTEGER_ONLY);
// Check for NaN, infinity, and -0.0
if (sawNaN) {
@ -97,7 +98,7 @@ void ParsedNumber::populateFormattable(Formattable& output) const {
}
}
U_ASSERT(!quantity.bogus);
if (quantity.isZero() && quantity.isNegative()) {
if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
output.setDouble(-0.0);
return;
}

View file

@ -52,9 +52,7 @@ ScientificMatcher::ScientificMatcher(const DecimalFormatSymbols& dfs, const Grou
bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const {
// Only accept scientific notation after the mantissa.
// Most places use result.hasNumber(), but we need a stronger condition here (i.e., exponent is
// not well-defined after NaN or infinity).
if (result.quantity.bogus) {
if (!result.seenNumber()) {
return false;
}
@ -95,8 +93,13 @@ bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErr
segment.adjustOffset(overlap2);
}
// We are supposed to accept E0 after NaN, so we need to make sure result.quantity is available.
bool wasBogus = result.quantity.bogus;
result.quantity.bogus = false;
int digitsOffset = segment.getOffset();
bool digitsReturnValue = fExponentMatcher.match(segment, result, exponentSign, status);
result.quantity.bogus = wasBogus;
if (segment.getOffset() != digitsOffset) {
// At least one exponent digit was matched.
result.flags |= FLAG_HAS_EXPONENT;

View file

@ -159,7 +159,7 @@ class ParsedNumber {
double getDouble() const;
void populateFormattable(Formattable& output) const;
void populateFormattable(Formattable& output, parse_flags_t parseFlags) const;
bool isBetterThan(const ParsedNumber& other);
};

View file

@ -366,7 +366,7 @@ void DecimalQuantityTest::testMaxDigits() {
dq.setFractionLength(0, 2);
assertEquals("Should trim, toPlainString", "76.54", dq.toPlainString());
assertEquals("Should trim, toScientificString", "7.654E+1", dq.toScientificString());
assertEquals("Should trim, toLong", 76L, dq.toLong());
assertEquals("Should trim, toLong", 76L, dq.toLong(true));
assertEquals("Should trim, toFractionLong", 54L, dq.toFractionLong(false));
assertEquals("Should trim, toDouble", 76.54, dq.toDouble());
// To test DecNum output, check the round-trip.

View file

@ -99,7 +99,7 @@ void NumberParserTest::testBasic() {
{3, u".00", u"0", 3, 0.0},
{3, u" 1,234", u"a0", 35, 1234.}, // should not hang
{3, u"NaN", u"0", 3, NAN},
{3, u"NaN E5", u"0", 3, NAN},
{3, u"NaN E5", u"0", 6, NAN},
{3, u"0", u"0", 1, 0.0}};
parse_flags_t parseFlags = PARSE_FLAG_IGNORE_CASE | PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES;

View file

@ -806,29 +806,29 @@ void NumberFormatRegressionTest::Test4092480 (void)
dfFoo->applyPattern("0000;-000", status);
failure(status, "dfFoo->applyPattern");
UnicodeString temp;
if (dfFoo->toPattern(temp) != UnicodeString("#0000"))
errln("dfFoo.toPattern : " + dfFoo->toPattern(temp));
if (dfFoo->toPattern(temp) != UnicodeString("0000"))
errln("ERROR: dfFoo.toPattern : " + dfFoo->toPattern(temp));
FieldPosition pos(FieldPosition::DONT_CARE);
logln(dfFoo->format((int32_t)42, temp, pos));
logln(dfFoo->format((int32_t)-42, temp, pos));
dfFoo->applyPattern("000;-000", status);
failure(status, "dfFoo->applyPattern");
if (dfFoo->toPattern(temp) != UnicodeString("#000"))
errln("dfFoo.toPattern : " + dfFoo->toPattern(temp));
if (dfFoo->toPattern(temp) != UnicodeString("000"))
errln("ERROR: dfFoo.toPattern : " + dfFoo->toPattern(temp));
logln(dfFoo->format((int32_t)42,temp, pos));
logln(dfFoo->format((int32_t)-42, temp, pos));
dfFoo->applyPattern("000;-0000", status);
failure(status, "dfFoo->applyPattern");
if (dfFoo->toPattern(temp) != UnicodeString("#000"))
errln("dfFoo.toPattern : " + dfFoo->toPattern(temp));
if (dfFoo->toPattern(temp) != UnicodeString("000"))
errln("ERROR: dfFoo.toPattern : " + dfFoo->toPattern(temp));
logln(dfFoo->format((int32_t)42, temp, pos));
logln(dfFoo->format((int32_t)-42, temp, pos));
dfFoo->applyPattern("0000;-000", status);
failure(status, "dfFoo->applyPattern");
if (dfFoo->toPattern(temp) != UnicodeString("#0000"))
errln("dfFoo.toPattern : " + dfFoo->toPattern(temp));
if (dfFoo->toPattern(temp) != UnicodeString("0000"))
errln("ERROR: dfFoo.toPattern : " + dfFoo->toPattern(temp));
logln(dfFoo->format((int32_t)42, temp, pos));
logln(dfFoo->format((int32_t)-42, temp, pos));
/*} catch (Exception foo) {
@ -1691,6 +1691,12 @@ void NumberFormatRegressionTest::Test4122840(void)
// Create a DecimalFormat using the pattern we got and format a number
DecimalFormatSymbols *symbols = new DecimalFormatSymbols(locales[i], status);
failure(status, "new DecimalFormatSymbols");
// Disable currency spacing for the purposes of this test.
// To do this, set the spacing insert to the empty string both before and after the symbol.
symbols->setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, FALSE, u"");
symbols->setPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, TRUE, u"");
DecimalFormat *fmt1 = new DecimalFormat(pattern, *symbols, status);
failure(status, "new DecimalFormat");
@ -2003,6 +2009,8 @@ void NumberFormatRegressionTest::Test4145457() {
* DecimalFormat.applyPattern() sets minimum integer digits incorrectly.
* CANNOT REPRODUCE
* This bug is a duplicate of 4139344, which is a duplicate of 4134300
*
* ICU 62: minInt is always at least one, and the getter should reflect that!
*/
void NumberFormatRegressionTest::Test4147295(void)
{
@ -2013,7 +2021,7 @@ void NumberFormatRegressionTest::Test4147295(void)
sdf->applyPattern(pattern, status);
if (!failure(status, "sdf->applyPattern")) {
int minIntDig = sdf->getMinimumIntegerDigits();
if (minIntDig != 0) {
if (minIntDig != 1) {
errln("Test failed");
errln(UnicodeString(" Minimum integer digits : ") + minIntDig);
UnicodeString temp;
@ -2205,26 +2213,28 @@ void NumberFormatRegressionTest::Test4167494(void) {
* DecimalFormat.parse() fails when ParseIntegerOnly set to true
*/
void NumberFormatRegressionTest::Test4170798(void) {
UErrorCode status = U_ZERO_ERROR;
NumberFormat *nf = NumberFormat::createInstance(Locale::getUS(), status);
if (failure(status, "NumberFormat::createInstance", TRUE)){
delete nf;
return;
};
DecimalFormat *df = dynamic_cast<DecimalFormat *>(nf);
if(df == NULL) {
errln("DecimalFormat needed to continue");
return;
IcuTestErrorCode status(*this, "Test4170798");
LocalPointer<DecimalFormat> df(dynamic_cast<DecimalFormat*>(
NumberFormat::createInstance(Locale::getUS(), status)), status);
{
Formattable n;
ParsePosition pos(0);
df->parse("-0.0", n, pos);
if (n.getType() != Formattable::kDouble
|| n.getDouble() != -0.0) {
errln(UnicodeString("FAIL: default parse(\"-0.0\") returns ") + toString(n));
}
}
df->setParseIntegerOnly(TRUE);
Formattable n;
ParsePosition pos(0);
df->parse("-0.0", n, pos);
if (n.getType() != Formattable::kLong
|| n.getLong() != 0) {
errln(UnicodeString("FAIL: parse(\"-0.0\") returns ") + toString(n));
{
Formattable n;
ParsePosition pos(0);
df->parse("-0.0", n, pos);
if (n.getType() != Formattable::kLong
|| n.getLong() != 0) {
errln(UnicodeString("FAIL: integer parse(\"-0.0\") returns ") + toString(n));
}
}
delete nf;
}
/**
@ -2233,15 +2243,15 @@ void NumberFormatRegressionTest::Test4170798(void) {
*/
void NumberFormatRegressionTest::Test4176114(void) {
const char* DATA[] = {
"00", "#00",
"000", "#000", // No grouping
"#000", "#000", // No grouping
"00", "00",
"000", "000", // No grouping
"#000", "000", // No grouping
"#,##0", "#,##0",
"#,000", "#,000",
"0,000", "#0,000",
"00,000", "#00,000",
"000,000", "#,000,000",
"0,000,000,000,000.0000", "#0,000,000,000,000.0000", // Reported
"0,000", "0,000",
"00,000", "00,000",
"000,000", "000,000",
"0,000,000,000,000.0000", "0,000,000,000,000.0000", // Reported
};
int DATA_length = UPRV_LENGTHOF(DATA);
UErrorCode status = U_ZERO_ERROR;
@ -2372,9 +2382,9 @@ void NumberFormatRegressionTest::Test4212072(void) {
sym.setSymbol(DecimalFormatSymbols::kCurrencySymbol, "usd");
fmt.setDecimalFormatSymbols(sym);
s.remove();
if (fmt.format(12.5, s, pos) != UnicodeString("usd12.50")) {
if (fmt.format(12.5, s, pos) != UnicodeString(u"usd\u00A012.50")) {
errln(UnicodeString("FAIL: 12.5 x (currency=usd) -> ") + s +
", exp usd12.50");
u", exp usd\u00A012.50");
}
s.remove();
if (fmt.getPositivePrefix(s) != UnicodeString("usd")) {
@ -2388,9 +2398,9 @@ void NumberFormatRegressionTest::Test4212072(void) {
sym.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, "DOL");
fmt.setDecimalFormatSymbols(sym);
s.remove();
if (fmt.format(12.5, s, pos) != UnicodeString("DOL12.50")) {
if (fmt.format(12.5, s, pos) != UnicodeString(u"DOL\u00A012.50")) {
errln(UnicodeString("FAIL: 12.5 x (intlcurrency=DOL) -> ") + s +
", exp DOL12.50");
u", exp DOL\u00A012.50");
}
s.remove();
if (fmt.getPositivePrefix(s) != UnicodeString("DOL")) {
@ -2734,7 +2744,7 @@ void NumberFormatRegressionTest::TestJ691(void) {
#define TEST_ASSERT_EQUALS(x,y) \
{ \
char _msg[1000]; \
int32_t len = sprintf (_msg,"File %s, line %d: Assertion Failed: " #x "==" #y "\n", __FILE__, __LINE__); \
int32_t len = sprintf (_msg,"File %s, line %d: " #x "==" #y, __FILE__, __LINE__); \
(void)len; \
U_ASSERT(len < (int32_t) sizeof(_msg)); \
assertEquals((const char*) _msg, x,y); \
@ -2759,10 +2769,10 @@ void NumberFormatRegressionTest::Test8199(void) {
Formattable val;
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT(1000000000 == val.getInt64(status));
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS(1000000000L, val.getInt64(status));
TEST_CHECK_STATUS(status);
TEST_ASSERT(1000000000.6 == val.getDouble(status));
TEST_ASSERT_EQUALS(1000000000.6, val.getDouble(status));
TEST_CHECK_STATUS(status);
numStr = "100000000000000001.1"; // approx 1E17, parses as a double rather
@ -2770,25 +2780,25 @@ void NumberFormatRegressionTest::Test8199(void) {
// even though int64 is more precise.
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT(100000000000000001LL == val.getInt64(status));
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS(100000000000000001LL, val.getInt64(status));
TEST_CHECK_STATUS(status);
TEST_ASSERT(100000000000000000.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(100000000000000000.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
numStr = "1E17"; // Parses with the internal decimal number having non-zero exponent
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kInt64 == val.getType());
TEST_ASSERT(100000000000000000LL == val.getInt64());
TEST_ASSERT(1.0E17 == val.getDouble(status));
TEST_ASSERT_EQUALS(Formattable::kInt64, val.getType());
TEST_ASSERT_EQUALS(100000000000000000LL, val.getInt64());
TEST_ASSERT_EQUALS(1.0E17, val.getDouble(status));
TEST_CHECK_STATUS(status);
numStr = "9223372036854775807"; // largest int64_t
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kInt64 == val.getType());
TEST_ASSERT(9223372036854775807LL == val.getInt64());
TEST_ASSERT_EQUALS(Formattable::kInt64, val.getType());
TEST_ASSERT_EQUALS(9223372036854775807LL, val.getInt64());
// In the following check, note that a substantial range of integers will
// convert to the same double value. There are also platform variations
// in the rounding at compile time of double constants.
@ -2799,31 +2809,31 @@ void NumberFormatRegressionTest::Test8199(void) {
numStr = "-9223372036854775808"; // smallest int64_t
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kInt64 == val.getType());
// TEST_ASSERT(-9223372036854775808LL == val.getInt64()); // Compiler chokes on constant.
TEST_ASSERT((int64_t)0x8000000000000000LL == val.getInt64());
TEST_ASSERT(-9223372036854775808.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(Formattable::kInt64, val.getType());
// TEST_ASSERT_EQUALS(-9223372036854775808LL, val.getInt64()); // Compiler chokes on constant.
TEST_ASSERT_EQUALS((int64_t)0x8000000000000000LL, val.getInt64());
TEST_ASSERT_EQUALS(-9223372036854775808.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
numStr = "9223372036854775808"; // largest int64_t + 1
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT(9223372036854775807LL == val.getInt64(status));
TEST_ASSERT(status == U_INVALID_FORMAT_ERROR);
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS(9223372036854775807LL, val.getInt64(status));
TEST_ASSERT_EQUALS(status, U_INVALID_FORMAT_ERROR);
status = U_ZERO_ERROR;
TEST_ASSERT(9223372036854775810.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(9223372036854775810.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
numStr = "-9223372036854775809"; // smallest int64_t - 1
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
// TEST_ASSERT(-9223372036854775808LL == val.getInt64(status)); // spurious compiler warnings
TEST_ASSERT((int64_t)0x8000000000000000LL == val.getInt64(status));
TEST_ASSERT(status == U_INVALID_FORMAT_ERROR);
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
// TEST_ASSERT_EQUALS(-9223372036854775808LL, val.getInt64(status)); // spurious compiler warnings
TEST_ASSERT_EQUALS((int64_t)0x8000000000000000LL, val.getInt64(status));
TEST_ASSERT_EQUALS(status, U_INVALID_FORMAT_ERROR);
status = U_ZERO_ERROR;
TEST_ASSERT(-9223372036854775810.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(-9223372036854775810.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
// Test values near the limit of where doubles can represent all integers.
@ -2837,25 +2847,25 @@ void NumberFormatRegressionTest::Test8199(void) {
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
// printf("getInt64() returns %lld\n", val.getInt64(status));
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT(9007199254740991LL == val.getInt64(status));
TEST_ASSERT(9007199254740991.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS(9007199254740991LL, val.getInt64(status));
TEST_ASSERT_EQUALS(9007199254740991.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
status = U_ZERO_ERROR;
numStr = "9007199254740992.1"; // 54 bits for the int part.
nf->parse(numStr, val, status);
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT(9007199254740992LL == val.getInt64(status));
TEST_ASSERT(9007199254740992.0 == val.getDouble(status));
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS(9007199254740992LL, val.getInt64(status));
TEST_ASSERT_EQUALS(9007199254740992.0, val.getDouble(status));
TEST_CHECK_STATUS(status);
status = U_ZERO_ERROR;
numStr = "9007199254740993.1"; // 54 bits for the int part. Double will round
nf->parse(numStr, val, status); // the ones digit, putting it up to ...994
TEST_CHECK_STATUS(status);
TEST_ASSERT(Formattable::kDouble == val.getType());
TEST_ASSERT_EQUALS(Formattable::kDouble, val.getType());
TEST_ASSERT_EQUALS((int64_t)9007199254740993LL,val.getInt64(status));
TEST_ASSERT_EQUALS((double)9007199254740994.0,(double)val.getDouble(status));
TEST_CHECK_STATUS(status);

View file

@ -990,8 +990,8 @@ void TestMessageFormat::testSetLocale()
// {sfb} to get $, would need Locale::US, not Locale::ENGLISH
// Just use unlocalized currency symbol.
//UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of $456.83.";
UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of ";
compareStrEng += (UChar) 0x00a4;
UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of XXX";
compareStrEng += (UChar) 0x00a0;
compareStrEng += "456.83.";
// {sfb} to get DM, would need Locale::GERMANY, not Locale::GERMAN
// Just use unlocalized currency symbol.
@ -999,8 +999,7 @@ void TestMessageFormat::testSetLocale()
UnicodeString compareStrGer = "At <time> on 08.08.1997, you made a deposit of ";
compareStrGer += "456,83";
compareStrGer += (UChar) 0x00a0;
compareStrGer += (UChar) 0x00a4;
compareStrGer += ".";
compareStrGer += "XXX.";
MessageFormat msg( formatStr, err);
result = "";

View file

@ -207,7 +207,7 @@ void IntlTestDecimalFormatSymbols::testSymbols(/* char *par */)
sym.setSymbol(DecimalFormatSymbols::kPercentSymbol, (UnicodeString)"P");
Verify(34.5, (UnicodeString)"00 %", sym, (UnicodeString)"3450 P");
sym.setSymbol(DecimalFormatSymbols::kCurrencySymbol, (UnicodeString)"D");
Verify(34.5, CharsToUnicodeString("\\u00a4##.##"), sym, (UnicodeString)"D34.5");
Verify(34.5, CharsToUnicodeString("\\u00a4##.##"), sym, (UnicodeString)"D 34.50");
sym.setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, (UnicodeString)"|");
Verify(3456.5, (UnicodeString)"0,000.##", sym, (UnicodeString)"3|456S5");

View file

@ -159,7 +159,7 @@ public class DecimalFormatProperties implements Cloneable, Serializable {
exponentSignAlwaysShown = false;
formatWidth = -1;
groupingSize = -1;
groupingUsed = false;
groupingUsed = true;
magnitudeMultiplier = 0;
mathContext = null;
maximumFractionDigits = -1;

View file

@ -19,8 +19,8 @@ import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
import com.ibm.icu.impl.number.PropertiesAffixPatternProvider;
import com.ibm.icu.impl.number.RoundingUtils;
import com.ibm.icu.number.Scale;
import com.ibm.icu.number.NumberFormatter.GroupingStrategy;
import com.ibm.icu.number.Scale;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.CurrencyAmount;
@ -155,6 +155,9 @@ public class NumberParserImpl {
if (properties.getParseIntegerOnly()) {
parseFlags |= ParsingUtils.PARSE_FLAG_INTEGER_ONLY;
}
if (properties.getParseToBigDecimal()) {
parseFlags |= ParsingUtils.PARSE_FLAG_FORCE_BIG_DECIMAL;
}
if (properties.getSignAlwaysShown()) {
parseFlags |= ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED;
}
@ -289,6 +292,10 @@ public class NumberParserImpl {
frozen = true;
}
public int getParseFlags() {
return parseFlags;
}
public void parse(String input, boolean greedy, ParsedNumber result) {
parse(input, 0, greedy, result);
}

View file

@ -131,12 +131,15 @@ public class ParsedNumber {
}
public Number getNumber() {
return getNumber(false);
return getNumber(0);
}
public Number getNumber(boolean forceBigDecimal) {
/** @param parseFlags Configuration settings from ParsingUtils.java */
public Number getNumber(int parseFlags) {
boolean sawNaN = 0 != (flags & FLAG_NAN);
boolean sawInfinity = 0 != (flags & FLAG_INFINITY);
boolean forceBigDecimal = 0 != (parseFlags & ParsingUtils.PARSE_FLAG_FORCE_BIG_DECIMAL);
boolean integerOnly = 0 != (parseFlags & ParsingUtils.PARSE_FLAG_INTEGER_ONLY);
// Check for NaN, infinity, and -0.0
if (sawNaN) {
@ -150,7 +153,7 @@ public class ParsedNumber {
}
}
assert quantity != null;
if (quantity.isZero() && quantity.isNegative()) {
if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
return -0.0;
}

View file

@ -22,6 +22,7 @@ public class ParsingUtils {
public static final int PARSE_FLAG_EXACT_AFFIX = 0x0200;
public static final int PARSE_FLAG_PLUS_SIGN_ALLOWED = 0x0400;
// public static final int PARSE_FLAG_OPTIMIZE = 0x0800; // no longer used
public static final int PARSE_FLAG_FORCE_BIG_DECIMAL = 0x1000;
public static void putLeadCodePoints(UnicodeSet input, UnicodeSet output) {
for (EntryRange range : input.ranges()) {

View file

@ -3,6 +3,7 @@
package com.ibm.icu.impl.number.parse;
import com.ibm.icu.impl.StringSegment;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.Grouper;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.UnicodeSet;
@ -46,9 +47,7 @@ public class ScientificMatcher implements NumberParseMatcher {
@Override
public boolean match(StringSegment segment, ParsedNumber result) {
// Only accept scientific notation after the mantissa.
// Most places use result.hasNumber(), but we need a stronger condition here (i.e., exponent is
// not well-defined after NaN or infinity).
if (result.quantity == null) {
if (!result.seenNumber()) {
return false;
}
@ -89,8 +88,17 @@ public class ScientificMatcher implements NumberParseMatcher {
segment.adjustOffset(overlap2);
}
// We are supposed to accept E0 after NaN, so we need to make sure result.quantity is available.
boolean wasNull = (result.quantity == null);
if (wasNull) {
result.quantity = new DecimalQuantity_DualStorageBCD();
}
int digitsOffset = segment.getOffset();
boolean digitsReturnValue = exponentMatcher.match(segment, result, exponentSign);
if (wasNull) {
result.quantity = null;
}
if (segment.getOffset() != digitsOffset) {
// At least one exponent digit was matched.
result.flags |= ParsedNumber.FLAG_HAS_EXPONENT;

View file

@ -139,8 +139,7 @@ final class NumberPropertyMapper {
boolean explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
boolean explicitMinMaxSig = minSig != -1 || maxSig != -1;
// Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
// maxFrac was
// set (but not both) on a currency instance.
// maxFrac was set (but not both) on a currency instance.
// NOTE: Increments are handled in "Rounder.constructCurrency()".
if (useCurrency) {
if (minFrac == -1 && maxFrac == -1) {

View file

@ -822,7 +822,7 @@ public class DecimalFormat extends NumberFormat {
if (result.success()) {
parsePosition.setIndex(result.charEnd);
// TODO: Accessing properties here is technically not thread-safe
Number number = result.getNumber(properties.getParseToBigDecimal());
Number number = result.getNumber(parser.getParseFlags());
// Backwards compatibility: return com.ibm.icu.math.BigDecimal
if (number instanceof java.math.BigDecimal) {
number = safeConvertBigDecimal((java.math.BigDecimal) number);
@ -861,7 +861,7 @@ public class DecimalFormat extends NumberFormat {
if (result.success()) {
parsePosition.setIndex(result.charEnd);
// TODO: Accessing properties here is technically not thread-safe
Number number = result.getNumber(properties.getParseToBigDecimal());
Number number = result.getNumber(parserWithCurrency.getParseFlags());
// Backwards compatibility: return com.ibm.icu.math.BigDecimal
if (number instanceof java.math.BigDecimal) {
number = safeConvertBigDecimal((java.math.BigDecimal) number);

View file

@ -460,9 +460,8 @@ public class NumberFormatDataDrivenTest {
if (tuple.maxSigDigits != null) {
properties.setMaximumSignificantDigits(tuple.maxSigDigits);
}
if (tuple.useGrouping != null && tuple.useGrouping == 0) {
properties.setGroupingSize(-1);
properties.setSecondaryGroupingSize(-1);
if (tuple.useGrouping != null) {
properties.setGroupingUsed(tuple.useGrouping > 0);
}
if (tuple.multiplier != null) {
properties.setMultiplier(new BigDecimal(tuple.multiplier));

View file

@ -1384,17 +1384,18 @@ public class NumberRegressionTests extends TestFmwk {
*/
@Test
public void Test4170798() {
Locale savedLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
DecimalFormat df = new DecimalFormat();
df.setParseIntegerOnly(true);
Number n = df.parse("-0.0", new ParsePosition(0));
if (!(n instanceof Double)
|| n.intValue() != 0) {
errln("FAIL: parse(\"-0.0\") returns " +
n + " (" + n.getClass().getName() + ')');
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(ULocale.US);
{
ParsePosition ppos = new ParsePosition(0);
Number result = df.parse("-0.0", ppos);
assertEquals("Should parse to double -0.0", new Double(-0.0), result);
}
df.setParseIntegerOnly(true);
{
ParsePosition ppos = new ParsePosition(0);
Number result = df.parse("-0.0", ppos);
assertEquals("Should parse to an integer type, not a double", new Long(0), result);
}
Locale.setDefault(savedLocale);
}
/**

View file

@ -105,7 +105,7 @@ public class NumberParserTest {
{ 3, ".00", "0", 3, 0.0 },
{ 3, " 1,234", "a0", 35, 1234. }, // should not hang
{ 3, "NaN", "0", 3, Double.NaN },
{ 3, "NaN E5", "0", 3, Double.NaN },
{ 3, "NaN E5", "0", 6, Double.NaN },
{ 3, "0", "0", 1, 0.0 } };
int parseFlags = ParsingUtils.PARSE_FLAG_IGNORE_CASE