mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 07:39:16 +00:00
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:
parent
d6c6fa0404
commit
12b34e7c9e
27 changed files with 170 additions and 125 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -25,7 +25,7 @@ void DecimalFormatProperties::clear() {
|
|||
exponentSignAlwaysShown = false;
|
||||
formatWidth = -1;
|
||||
groupingSize = -1;
|
||||
groupingUsed = false;
|
||||
groupingUsed = true;
|
||||
magnitudeMultiplier = 0;
|
||||
maximumFractionDigits = -1;
|
||||
maximumIntegerDigits = -1;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue