// Cannot use the set() functions because they would delete the fDecimalNum value,
// TODO: fDecimalQuantity->fitsInInt() to kLong type.
/*
- if (fDecimalQuantity->fitsIntoLong(FALSE)) {
+ if (fDecimalQuantity->fitsInInt()) {
fType = kLong;
fValue.fInt64 = fDecimalNum->getLong();
} else
}
dispose();
- DecimalQuantity* dq = new DecimalQuantity();
+ auto* dq = new DecimalQuantity();
dq->setToDecNumber(numberString, status);
adoptDecimalQuantity(dq);
&ignoredIsChoiceFormatFillIn,
&symbolLen,
&status);
- // Readonly-aliasing char16_t* constructor:
+ // Readonly-aliasing char16_t* constructor, which points to a resource bundle:
return UnicodeString(TRUE, symbol, symbolLen);
}
if (!fIntlCurrencySymbol.isBogus()) {
return fIntlCurrencySymbol;
}
- // Readonly-aliasing char16_t* constructor:
- return UnicodeString(TRUE, fCurrency.getISOCurrency(), 3);
+ // Note: Not safe to use readonly-aliasing constructor here because the buffer belongs to this object,
+ // which could be destructed or moved during the lifetime of the return value.
+ return UnicodeString(fCurrency.getISOCurrency(), 3);
}
UnicodeString CurrencySymbols::getPluralName(StandardPlural::Form plural, UErrorCode& status) const {
StandardPlural::getKeyword(plural),
&symbolLen,
&status);
- // Readonly-aliasing char16_t* constructor:
+ // Readonly-aliasing char16_t* constructor, which points to a resource bundle:
return UnicodeString(TRUE, symbol, symbolLen);
}
// Check for invalid syntax and set the corresponding error code.
if ((set.status & DEC_Conversion_syntax) != 0) {
status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
+ } else if (set.status != 0) {
+ // Not a syntax error, but some other error, like an exponent that is too large.
+ status = U_UNSUPPORTED_ERROR;
}
}
if (_scale >= 0) {
// 1e22 is the largest exact double.
int32_t i = _scale;
- for (; i >= 22; i -= 22) result *= 1e22;
+ for (; i >= 22; i -= 22) {
+ result *= 1e22;
+ if (uprv_isInfinite(result)) {
+ // Further multiplications will not be productive.
+ i = 0;
+ break;
+ }
+ }
result *= DOUBLE_MULTIPLIERS[i];
} else {
// 1e22 is the largest exact double.
int32_t i = _scale;
- for (; i <= -22; i += 22) result /= 1e22;
+ for (; i <= -22; i += 22) {
+ result /= 1e22;
+ if (result == 0.0) {
+ // Further divisions will not be productive.
+ i = 0;
+ break;
+ }
+ }
result /= DOUBLE_MULTIPLIERS[-i];
}
if (isNegative()) { result = -result; }
}
UnicodeString DecimalQuantity::toNumberString() const {
- MaybeStackArray<char, 30> digits(precision + 11);
+ // 13 should hold both the largest and the smallest int32_t plus exponent separator and NUL
+ MaybeStackArray<char, 30> digits(precision + 13);
for (int32_t i = 0; i < precision; i++) {
digits[i] = getDigitPos(precision - i - 1) + '0';
}
- snprintf(digits.getAlias() + precision, 11, "E%d", scale);
+ snprintf(digits.getAlias() + precision, 13, "E%d", scale);
return UnicodeString(digits.getAlias(), -1, US_INV);
}
}
bool CurrencyCustomMatcher::smokeTest(const StringSegment& segment) const {
- return segment.startsWith(fCurrency1) || segment.startsWith(fCurrency2);
+ return segment.startsWith(fCurrency1)
+ || segment.startsWith(fCurrency2);
}
UnicodeString CurrencyCustomMatcher::toString() const {
UErrorCode& status) const {
U_ASSERT(fFrozen);
// TODO: Check start >= 0 and start < input.length()
- StringSegment segment(input, fParseFlags);
+ StringSegment segment(input, 0 != (fParseFlags & PARSE_FLAG_IGNORE_CASE));
segment.adjustOffset(start);
if (greedy) {
parseGreedyRecursive(segment, result, status);
for (auto& cas : cases) {
UnicodeString input(cas.input);
- StringSegment segment(input, 0);
+ StringSegment segment(input, false);
ParsedNumber result;
bool actualMaybeMore = series.match(segment, result, status);
int actualOffset = segment.getOffset();
for (auto& cas : cases) {
UnicodeString input(cas.input);
- StringSegment segment(input, 0);
+ StringSegment segment(input, false);
ParsedNumber result;
matcher.match(segment, result, status);
assertEquals("Parsing " + input, cas.expectedCurrencyCode, result.currencyCode);
assertEquals(affixPattern + " " + cas.exactMatch, cas.expectedMatcherLength, matcher.length());
// Check that the matcher works on a sample string
- StringSegment segment(sampleParseableString, 0);
+ StringSegment segment(sampleParseableString, false);
ParsedNumber result;
matcher.match(segment, result, status);
assertEquals(affixPattern + " " + cas.exactMatch, sampleParseableString.length(), result.charEnd);
}
void StringSegmentTest::testOffset() {
- StringSegment segment(SAMPLE_STRING, 0);
+ StringSegment segment(SAMPLE_STRING, false);
assertEquals("Initial Offset", 0, segment.getOffset());
segment.adjustOffset(3);
assertEquals("Adjust A", 3, segment.getOffset());
}
void StringSegmentTest::testLength() {
- StringSegment segment(SAMPLE_STRING, 0);
+ StringSegment segment(SAMPLE_STRING, false);
assertEquals("Initial length", 11, segment.length());
segment.adjustOffset(3);
assertEquals("Adjust", 8, segment.length());
}
void StringSegmentTest::testCharAt() {
- StringSegment segment(SAMPLE_STRING, 0);
+ StringSegment segment(SAMPLE_STRING, false);
assertEquals("Initial", SAMPLE_STRING, segment.toUnicodeString());
assertEquals("Initial", SAMPLE_STRING, segment.toTempUnicodeString());
segment.adjustOffset(3);
}
void StringSegmentTest::testGetCodePoint() {
- StringSegment segment(SAMPLE_STRING, 0);
+ StringSegment segment(SAMPLE_STRING, false);
assertEquals("Double-width code point", 0x1F4FB, segment.getCodePoint());
segment.setLength(1);
assertEquals("Inalid A", -1, segment.getCodePoint());
}
void StringSegmentTest::testCommonPrefixLength() {
- StringSegment segment(SAMPLE_STRING, 0);
+ StringSegment segment(SAMPLE_STRING, false);
assertEquals("", 11, segment.getCommonPrefixLength(SAMPLE_STRING));
assertEquals("", 4, segment.getCommonPrefixLength(u"📻 r"));
assertEquals("", 3, segment.getCommonPrefixLength(u"📻 x"));
}
CharString formatValue;
formatValue.appendInvariantChars(str, status);
- digitList.setToDecNumber(StringPiece(formatValue.data()), status);
+ digitList.setToDecNumber({formatValue.data(), formatValue.length()}, status);
return digitList;
}
}
}
double doubleVal = digitList.toDouble();
- {
+ DecimalQuantity doubleCheck;
+ doubleCheck.setToDouble(doubleVal);
+ if (digitList == doubleCheck) { // skip cases where the double does not round-trip
UnicodeString appendTo;
format(*fmtPtr, doubleVal, appendTo, status);
if (U_FAILURE(status)) {
return FALSE;
}
}
- if (!uprv_isNaN(doubleVal) && !uprv_isInfinite(doubleVal) && doubleVal == uprv_floor(doubleVal)) {
+ if (!uprv_isNaN(doubleVal) && !uprv_isInfinite(doubleVal) && digitList.fitsInLong()) {
int64_t intVal = digitList.toLong();
{
UnicodeString appendTo;
appendErrorMessage = appendErrorMessage + ppos.getErrorIndex();
return FALSE;
}
- UnicodeString resultStr(UnicodeString::fromUTF8(result.getDecimalNumber(status)));
if (tuple.output == "fail") {
- appendErrorMessage.append(UnicodeString("Parse succeeded: ") + resultStr + ", but was expected to fail.");
+ appendErrorMessage.append(UnicodeString("Parse succeeded: ") + result.getDouble() + ", but was expected to fail.");
return TRUE; // TRUE because failure handling is in the test suite
}
if (tuple.output == "NaN") {
if (!uprv_isNaN(result.getDouble())) {
- appendErrorMessage.append("Expected NaN, but got: " + resultStr);
+ appendErrorMessage.append(UnicodeString("Expected NaN, but got: ") + result.getDouble());
return FALSE;
}
return TRUE;
} else if (tuple.output == "Inf") {
if (!uprv_isInfinite(result.getDouble()) || result.getDouble() < 0) {
- appendErrorMessage.append("Expected Inf, but got: " + resultStr);
+ appendErrorMessage.append(UnicodeString("Expected Inf, but got: ") + result.getDouble());
return FALSE;
}
return TRUE;
} else if (tuple.output == "-Inf") {
if (!uprv_isInfinite(result.getDouble()) || result.getDouble() > 0) {
- appendErrorMessage.append("Expected -Inf, but got: " + resultStr);
+ appendErrorMessage.append(UnicodeString("Expected -Inf, but got: ") + result.getDouble());
+ return FALSE;
+ }
+ return TRUE;
+ } else if (tuple.output == "-0.0") {
+ if (std::signbit(result.getDouble()) == 0 || result.getDouble() != 0) {
+ appendErrorMessage.append(UnicodeString("Expected -0.0, but got: ") + result.getDouble());
return FALSE;
}
return TRUE;
}
- DecimalQuantity expected;
- strToDigitList(tuple.output, expected, status);
+ // All other cases parse to a DecimalQuantity, not a double.
+
+ DecimalQuantity expectedQuantity;
+ strToDigitList(tuple.output, expectedQuantity, status);
+ UnicodeString expectedString = expectedQuantity.toNumberString();
if (U_FAILURE(status)) {
- appendErrorMessage.append("Error parsing.");
- return FALSE;
+ appendErrorMessage.append("[Error parsing decnumber] ");
+ // If this happens, assume that tuple.output is exactly the same format as
+ // DecimalQuantity.toNumberString()
+ expectedString = tuple.output;
+ status = U_ZERO_ERROR;
}
- if (expected != *result.getDecimalQuantity()) {
- appendErrorMessage.append(UnicodeString("Expected: ") + tuple.output + ", but got: " + resultStr + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
+ UnicodeString actualString = result.getDecimalQuantity()->toNumberString();
+ if (expectedString != actualString) {
+ appendErrorMessage.append(
+ UnicodeString("Expected: ") + tuple.output + " (i.e., " + expectedString + "), but got: " +
+ actualString + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
return FALSE;
}
+
return TRUE;
}
return FALSE;
}
UnicodeString currStr(currAmt->getISOCurrency());
- Formattable resultFormattable(currAmt->getNumber());
- UnicodeString resultStr(UnicodeString::fromUTF8(resultFormattable.getDecimalNumber(status)));
+ U_ASSERT(currAmt->getNumber().getDecimalQuantity() != nullptr); // no doubles in currency tests
+ UnicodeString resultStr = currAmt->getNumber().getDecimalQuantity()->toNumberString();
if (tuple.output == "fail") {
appendErrorMessage.append(UnicodeString("Parse succeeded: ") + resultStr + ", but was expected to fail.");
return TRUE; // TRUE because failure handling is in the test suite
}
- DecimalQuantity expected;
- strToDigitList(tuple.output, expected, status);
+
+ DecimalQuantity expectedQuantity;
+ strToDigitList(tuple.output, expectedQuantity, status);
+ UnicodeString expectedString = expectedQuantity.toNumberString();
if (U_FAILURE(status)) {
- appendErrorMessage.append("Error parsing.");
- return FALSE;
+ appendErrorMessage.append("Error parsing decnumber");
+ // If this happens, assume that tuple.output is exactly the same format as
+ // DecimalQuantity.toNumberString()
+ expectedString = tuple.output;
+ status = U_ZERO_ERROR;
}
- if (expected != *currAmt->getNumber().getDecimalQuantity()) {
- appendErrorMessage.append(UnicodeString("Expected: ") + tuple.output + ", but got: " + resultStr + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
+ if (expectedString != resultStr) {
+ appendErrorMessage.append(
+ UnicodeString("Expected: ") + tuple.output + " (i.e., " + expectedString + "), but got: " +
+ resultStr + " (" + ppos.getIndex() + ":" + ppos.getErrorIndex() + ")");
return FALSE;
}
+
if (currStr != tuple.outputCurrency) {
appendErrorMessage.append(UnicodeString(
"Expected currency: ") + tuple.outputCurrency + ", got: " + currStr + ". ");
begin
currency currencyUsage toPattern breaks
// These work in J, but it prepends an extra hash sign to the pattern.
-// C does not print the currency rounding information in the pattern.
// K does not support this feature.
USD standard 0.00 JK
CHF standard 0.00 JK
test parse infinity and scientific notation overflow
set locale en
+set lenient 1
begin
parse output breaks
NaN NaN K
-1E-99999999999999 -0.0
1E2147483648 Inf K
1E2147483647 Inf K
-1E2147483646 1E2147483646 C
+1E2147483646 1E2147483646
1E-2147483649 0
1E-2147483648 0
-// P returns zero here
-1E-2147483647 1E-2147483647 P
-1E-2147483646 1E-2147483646 C
+// C and P return zero here
+1E-2147483647 1E-2147483647 CP
+1E-2147483646 1E-2147483646
test format push limits
set locale en
set roundingMode halfDown
begin
maxFractionDigits format output breaks
-// C has trouble formatting too many digits (#11318)
-100 987654321987654321 987654321987654321.00 C
-100 987654321.987654321 987654321.987654321 C
-100 9999999999999.9950000000001 9999999999999.9950000000001 C
+100 987654321987654321 987654321987654321.00
+100 987654321.987654321 987654321.987654321
+100 9999999999999.9950000000001 9999999999999.9950000000001
2 9999999999999.9950000000001 10000000000000.00
2 9999999.99499999 9999999.99
// K doesn't support halfDown rounding mode?
2 9999999.995 9999999.99 K
2 9999999.99500001 10000000.00
-100 56565656565656565656565656565656565656565656565656565656565656 56565656565656565656565656565656565656565656565656565656565656.00 C
-100 454545454545454545454545454545.454545454545454545454545454545 454545454545454545454545454545.454545454545454545454545454545 C
+100 56565656565656565656565656565656565656565656565656565656565656 56565656565656565656565656565656565656565656565656565656565656.00
+100 454545454545454545454545454545.454545454545454545454545454545 454545454545454545454545454545.454545454545454545454545454545
100 0.0000000000000000000123 0.0000000000000000000123
-100 -78787878787878787878787878787878 -78787878787878787878787878787878.00 C
-100 -8989898989898989898989.8989898989898989 -8989898989898989898989.8989898989898989 C
+100 -78787878787878787878787878787878 -78787878787878787878787878787878.00
+100 -8989898989898989898989.8989898989898989 -8989898989898989898989.8989898989898989
test ticket 11230
set locale en
if (_scale >= 0) {
// 1e22 is the largest exact double.
int i = _scale;
- for (; i >= 22; i -= 22)
+ for (; i >= 22; i -= 22) {
result *= 1e22;
+ if (Double.isInfinite(result)) {
+ // Further multiplications will not be productive.
+ i = 0;
+ break;
+ }
+ }
result *= DOUBLE_MULTIPLIERS[i];
} else {
// 1e22 is the largest exact double.
int i = _scale;
- for (; i <= -22; i += 22)
+ for (; i <= -22; i += 22) {
result /= 1e22;
+ if (result == 0.0) {
+ // Further divisions will not be productive.
+ i = 0;
+ break;
+ }
+ }
result /= DOUBLE_MULTIPLIERS[-i];
}
if (isNegative())
d = d.negate();
}
// Special case: MIN_LONG
+ // TODO: It is supported in quantity.toLong() if quantity had the negative flag.
if (d.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) == 0 && !forceBigDecimal) {
return Long.MIN_VALUE;
}
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.dev.text.DecimalFormat_ICU58;
import com.ibm.icu.impl.number.DecimalFormatProperties;
+import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
import com.ibm.icu.impl.number.parse.NumberParserImpl;
-import com.ibm.icu.impl.number.parse.NumberParserImpl.ParseMode;
import com.ibm.icu.number.LocalizedNumberFormatter;
import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.DecimalFormat;