From: Shane Carr Date: Wed, 11 Apr 2018 05:52:58 +0000 (+0000) Subject: ICU-13634 Changing DecimalQuantity#toNumberString() to be DecimalQuantity#toScientifi... X-Git-Tag: release-62-rc~200^2~44 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cd92fa2c88788780b2572d508f129cddb89c2e36;p=icu ICU-13634 Changing DecimalQuantity#toNumberString() to be DecimalQuantity#toScientificString() with slightly friendlier output syntax for better compatibility. More currency tweaks. X-SVN-Rev: 41215 --- diff --git a/icu4c/source/i18n/currunit.cpp b/icu4c/source/i18n/currunit.cpp index 1750b94fab4..003ce612109 100644 --- a/icu4c/source/i18n/currunit.cpp +++ b/icu4c/source/i18n/currunit.cpp @@ -18,20 +18,23 @@ #include "unicode/ustring.h" #include "cstring.h" +static constexpr char16_t kDefaultCurrency[] = u"XXX"; + U_NAMESPACE_BEGIN CurrencyUnit::CurrencyUnit(ConstChar16Ptr _isoCode, UErrorCode& ec) { - *isoCode = 0; - if (U_SUCCESS(ec)) { - if (_isoCode != nullptr && u_strlen(_isoCode)==3) { - u_strcpy(isoCode, _isoCode); - char simpleIsoCode[4]; - u_UCharsToChars(isoCode, simpleIsoCode, 4); - initCurrency(simpleIsoCode); - } else { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } + // The constructor always leaves the CurrencyUnit in a valid state (with a 3-character currency code). + if (U_FAILURE(ec) || _isoCode == nullptr) { + u_strcpy(isoCode, kDefaultCurrency); + } else if (u_strlen(_isoCode) != 3) { + u_strcpy(isoCode, kDefaultCurrency); + ec = U_ILLEGAL_ARGUMENT_ERROR; + } else { + u_strcpy(isoCode, _isoCode); } + char simpleIsoCode[4]; + u_UCharsToChars(isoCode, simpleIsoCode, 4); + initCurrency(simpleIsoCode); } CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) : MeasureUnit(other) { @@ -52,7 +55,7 @@ CurrencyUnit::CurrencyUnit(const MeasureUnit& other, UErrorCode& ec) : MeasureUn } CurrencyUnit::CurrencyUnit() : MeasureUnit() { - u_strcpy(isoCode, u"XXX"); + u_strcpy(isoCode, kDefaultCurrency); char simpleIsoCode[4]; u_UCharsToChars(isoCode, simpleIsoCode, 4); initCurrency(simpleIsoCode); diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index bf975a31d99..c88857c2719 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -402,7 +402,9 @@ UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { FormattedNumber output = fFormatter->formatDouble(number, status); - output.populateFieldPositionIterator(*posIter, status); + if (posIter != nullptr) { + output.populateFieldPositionIterator(*posIter, status); + } auto appendable = UnicodeStringAppendable(appendTo); output.appendTo(appendable); return appendTo; @@ -445,7 +447,9 @@ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { FormattedNumber output = fFormatter->formatInt(number, status); - output.populateFieldPositionIterator(*posIter, status); + if (posIter != nullptr) { + output.populateFieldPositionIterator(*posIter, status); + } auto appendable = UnicodeStringAppendable(appendTo); output.appendTo(appendable); return appendTo; @@ -456,7 +460,9 @@ DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPosition UErrorCode& status) const { ErrorCode localStatus; FormattedNumber output = fFormatter->formatDecimal(number, localStatus); - output.populateFieldPositionIterator(*posIter, status); + if (posIter != nullptr) { + output.populateFieldPositionIterator(*posIter, status); + } auto appendable = UnicodeStringAppendable(appendTo); output.appendTo(appendable); return appendTo; @@ -465,7 +471,9 @@ DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPosition UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { FormattedNumber output = fFormatter->formatDecimalQuantity(number, status); - output.populateFieldPositionIterator(*posIter, status); + if (posIter != nullptr) { + output.populateFieldPositionIterator(*posIter, status); + } auto appendable = UnicodeStringAppendable(appendTo); output.appendTo(appendable); return appendTo; @@ -916,6 +924,7 @@ void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { } void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { + NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility fProperties->currency = CurrencyUnit(theCurrency, ec); // TODO: Set values in fSymbols, too? refreshFormatterNoError(); @@ -923,6 +932,7 @@ void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { void DecimalFormat::setCurrency(const char16_t* theCurrency) { ErrorCode localStatus; + NumberFormat::setCurrency(theCurrency, localStatus); // to set field for compatibility setCurrency(theCurrency, localStatus); } @@ -998,6 +1008,7 @@ void DecimalFormat::refreshFormatter(UErrorCode& status) { *fProperties, *fSymbols, true, status), status); // In order for the getters to work, we need to populate some fields in NumberFormat. + NumberFormat::setCurrency(fExportedProperties->currency.get(status).getISOCurrency(), status); NumberFormat::setMaximumIntegerDigits(fExportedProperties->maximumIntegerDigits); NumberFormat::setMinimumIntegerDigits(fExportedProperties->minimumIntegerDigits); NumberFormat::setMaximumFractionDigits(fExportedProperties->maximumFractionDigits); diff --git a/icu4c/source/i18n/fmtable.cpp b/icu4c/source/i18n/fmtable.cpp index 68e06767a1c..ecee7388dab 100644 --- a/icu4c/source/i18n/fmtable.cpp +++ b/icu4c/source/i18n/fmtable.cpp @@ -745,8 +745,14 @@ CharString *Formattable::internalGetCharString(UErrorCode &status) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - UnicodeString result = fDecimalQuantity->toNumberString(); - fDecimalStr->appendInvariantChars(result, 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 (std::abs(fDecimalQuantity->getMagnitude()) < 5) { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status); + } else { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status); + } } return fDecimalStr; } diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index 9c16fe4c3e7..baae9b76321 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp @@ -553,13 +553,12 @@ double DecimalQuantity::toDouble() const { // We are processing well-formed input, so we don't need any special options to StringToDoubleConverter. StringToDoubleConverter converter(0, 0, 0, "", ""); - UnicodeString numberString = toNumberString(); + UnicodeString numberString = this->toScientificString(); int32_t count; - double result = converter.StringToDouble(reinterpret_cast(numberString.getBuffer()), numberString.length(), &count); - if (isNegative()) { - result = -result; - } - return result; + return converter.StringToDouble( + reinterpret_cast(numberString.getBuffer()), + numberString.length(), + &count); } double DecimalQuantity::toDoubleFromOriginal() const { @@ -775,7 +774,7 @@ UnicodeString DecimalQuantity::toPlainString() const { if (isNegative()) { sb.append(u'-'); } - if (precision == 0) { + if (precision == 0 || getMagnitude() < 0) { sb.append(u'0'); } for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { @@ -785,6 +784,43 @@ UnicodeString DecimalQuantity::toPlainString() const { return sb; } +UnicodeString DecimalQuantity::toScientificString() const { + U_ASSERT(!isApproximate); + UnicodeString result; + if (isNegative()) { + result.append(u'-'); + } + if (precision == 0) { + result.append(u"0E+0", -1); + return result; + } + result.append(u'0' + getDigitPos(precision - 1)); + if (precision > 1) { + result.append(u'.'); + for (int32_t i = 1; i < precision; i++) { + result.append(u'0' + getDigitPos(precision - i - 1)); + } + } + result.append(u'E'); + int32_t _scale = scale + precision - 1; + if (_scale < 0) { + _scale *= -1; + result.append(u'-'); + } else { + result.append(u'+'); + } + if (_scale == 0) { + result.append(u'0'); + } + int32_t insertIndex = result.length(); + while (_scale > 0) { + std::div_t res = std::div(_scale, 10); + result.insert(insertIndex, u'0' + res.rem); + _scale = res.quot; + } + return result; +} + //////////////////////////////////////////////////// /// End of DecimalQuantity_AbstractBCD.java /// /// Start of DecimalQuantity_DualStorageBCD.java /// @@ -1128,31 +1164,4 @@ UnicodeString DecimalQuantity::toString() const { return UnicodeString(buffer8, -1, US_INV); } -UnicodeString DecimalQuantity::toNumberString() const { - U_ASSERT(!isApproximate); - UnicodeString result; - if (precision == 0) { - result.append(u'0'); - } - for (int32_t i = 0; i < precision; i++) { - result.append(u'0' + getDigitPos(precision - i - 1)); - } - result.append(u'E'); - int32_t _scale = scale; - if (_scale < 0) { - _scale *= -1; - result.append(u'-'); - } - if (_scale == 0) { - result.append(u'0'); - } - int32_t insertIndex = result.length(); - while (_scale > 0) { - std::div_t res = std::div(_scale, 10); - result.insert(insertIndex, u'0' + res.rem); - _scale = res.quot; - } - return result; -} - #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_decimalquantity.h b/icu4c/source/i18n/number_decimalquantity.h index 268cdd9967b..78bcdfc0b35 100644 --- a/icu4c/source/i18n/number_decimalquantity.h +++ b/icu4c/source/i18n/number_decimalquantity.h @@ -252,10 +252,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { UnicodeString toString() const; - /* Returns the string in exponential notation. */ - UnicodeString toNumberString() const; + /** Returns the string in standard exponential notation. */ + UnicodeString toScientificString() const; - /* Returns the string without exponential notation. Slightly slower than toNumberString(). */ + /** Returns the string without exponential notation. Slightly slower than toScientificString(). */ UnicodeString toPlainString() const; /** Visible for testing */ diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index 62947be5711..48a45c00449 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp @@ -185,7 +185,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; - CurrencyUnit currency(kDefaultCurrency, status); + CurrencyUnit currency(nullptr, status); if (isCurrency) { currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit } diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp index ef13267dcd8..24a09e6f4b2 100644 --- a/icu4c/source/i18n/number_mapper.cpp +++ b/icu4c/source/i18n/number_mapper.cpp @@ -265,6 +265,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert if (exportedProperties != nullptr) { + exportedProperties->currency = currency; exportedProperties->roundingMode = roundingMode; exportedProperties->minimumIntegerDigits = minInt; exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt; diff --git a/icu4c/source/i18n/number_stringbuilder.cpp b/icu4c/source/i18n/number_stringbuilder.cpp index a1900deab82..dd189067a79 100644 --- a/icu4c/source/i18n/number_stringbuilder.cpp +++ b/icu4c/source/i18n/number_stringbuilder.cpp @@ -459,7 +459,7 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const { // TODO: Set an initial capacity on uvec? - LocalPointer uvec(new UVector32(status)); + LocalPointer uvec(new UVector32(status), status); if (U_FAILURE(status)) { return; } diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h index 1081919c4cc..ac7b0959325 100644 --- a/icu4c/source/i18n/number_types.h +++ b/icu4c/source/i18n/number_types.h @@ -38,9 +38,6 @@ static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN; // ICU4J Equivalent: Padder.FALLBACK_PADDING_STRING static constexpr char16_t kFallbackPaddingString[] = u" "; -// ICU4J Equivalent: NumberFormatterImpl.DEFAULT_CURRENCY -static constexpr char16_t kDefaultCurrency[] = u"XXX"; - // Forward declarations: class Modifier; diff --git a/icu4c/source/i18n/number_utils.cpp b/icu4c/source/i18n/number_utils.cpp index c2f74e5bb67..8cf702dd336 100644 --- a/icu4c/source/i18n/number_utils.cpp +++ b/icu4c/source/i18n/number_utils.cpp @@ -104,18 +104,20 @@ void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) { static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1"); uprv_decNumberFromString(fData.getAlias(), str, &fContext); - // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity! - if (decNumberIsSpecial(fData.getAlias())) { - status = U_UNSUPPORTED_ERROR; - return; - } - // Check for invalid syntax and set the corresponding error code. if ((fContext.status & DEC_Conversion_syntax) != 0) { status = U_DECIMAL_NUMBER_SYNTAX_ERROR; + return; } else if (fContext.status != 0) { // Not a syntax error, but some other error, like an exponent that is too large. status = U_UNSUPPORTED_ERROR; + return; + } + + // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity! + if (decNumberIsSpecial(fData.getAlias())) { + status = U_UNSUPPORTED_ERROR; + return; } } diff --git a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp index 320dbe0eaba..2a19b0b9087 100644 --- a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp +++ b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp @@ -105,18 +105,18 @@ void DecimalQuantityTest::testSwitchStorage() { fq.setToLong(1234123412341234L); assertFalse("Should not be using byte array", fq.isUsingBytes()); - assertEquals("Failed on initialize", u"1234123412341234E0", fq.toNumberString()); + assertEquals("Failed on initialize", u"1.234123412341234E+15", fq.toScientificString()); assertHealth(fq); // Long -> Bytes fq.appendDigit(5, 0, true); assertTrue("Should be using byte array", fq.isUsingBytes()); - assertEquals("Failed on multiply", u"12341234123412345E0", fq.toNumberString()); + assertEquals("Failed on multiply", u"1.2341234123412345E+16", fq.toScientificString()); assertHealth(fq); // Bytes -> Long fq.roundToMagnitude(5, RoundingMode::UNUM_ROUND_HALFEVEN, status); assertSuccess("Rounding to magnitude", status); assertFalse("Should not be using byte array", fq.isUsingBytes()); - assertEquals("Failed on round", u"123412341234E5", fq.toNumberString()); + assertEquals("Failed on round", u"1.23412341234E+16", fq.toScientificString()); assertHealth(fq); } @@ -170,50 +170,46 @@ void DecimalQuantityTest::testCopyMove() { void DecimalQuantityTest::testAppend() { DecimalQuantity fq; fq.appendDigit(1, 0, true); - assertEquals("Failed on append", u"1E0", fq.toNumberString()); + assertEquals("Failed on append", u"1E+0", fq.toScientificString()); assertHealth(fq); fq.appendDigit(2, 0, true); - assertEquals("Failed on append", u"12E0", fq.toNumberString()); + assertEquals("Failed on append", u"1.2E+1", fq.toScientificString()); assertHealth(fq); fq.appendDigit(3, 1, true); - assertEquals("Failed on append", u"1203E0", fq.toNumberString()); + assertEquals("Failed on append", u"1.203E+3", fq.toScientificString()); assertHealth(fq); fq.appendDigit(0, 1, true); - assertEquals("Failed on append", u"1203E2", fq.toNumberString()); + assertEquals("Failed on append", u"1.203E+5", fq.toScientificString()); assertHealth(fq); fq.appendDigit(4, 0, true); - assertEquals("Failed on append", u"1203004E0", fq.toNumberString()); + assertEquals("Failed on append", u"1.203004E+6", fq.toScientificString()); assertHealth(fq); fq.appendDigit(0, 0, true); - assertEquals("Failed on append", u"1203004E1", fq.toNumberString()); + assertEquals("Failed on append", u"1.203004E+7", fq.toScientificString()); assertHealth(fq); fq.appendDigit(5, 0, false); - assertEquals("Failed on append", u"120300405E-1", fq.toNumberString()); + assertEquals("Failed on append", u"1.20300405E+7", fq.toScientificString()); assertHealth(fq); fq.appendDigit(6, 0, false); - assertEquals("Failed on append", u"1203004056E-2", fq.toNumberString()); + assertEquals("Failed on append", u"1.203004056E+7", fq.toScientificString()); assertHealth(fq); fq.appendDigit(7, 3, false); - assertEquals("Failed on append", u"12030040560007E-6", fq.toNumberString()); + assertEquals("Failed on append", u"1.2030040560007E+7", fq.toScientificString()); assertHealth(fq); - UnicodeString baseExpected(u"12030040560007"); + UnicodeString baseExpected(u"1.2030040560007"); for (int i = 0; i < 10; i++) { fq.appendDigit(8, 0, false); baseExpected.append(u'8'); UnicodeString expected(baseExpected); - expected.append(u"E-"); - if (i >= 3) { - expected.append(u'1'); - } - expected.append(((7 + i) % 10) + u'0'); - assertEquals("Failed on append", expected, fq.toNumberString()); + expected.append(u"E+7"); + assertEquals("Failed on append", expected, fq.toScientificString()); assertHealth(fq); } fq.appendDigit(9, 2, false); baseExpected.append(u"009"); UnicodeString expected(baseExpected); - expected.append(u"E-19"); - assertEquals("Failed on append", expected, fq.toNumberString()); + expected.append(u"E+7"); + assertEquals("Failed on append", expected, fq.toScientificString()); assertHealth(fq); } diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index cf9bd3bc8a4..dc014a3c92d 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -458,15 +458,15 @@ UBool NumberFormatTestDataDriven::isParsePass( DecimalQuantity expectedQuantity; strToDigitList(tuple.output, expectedQuantity, status); - UnicodeString expectedString = expectedQuantity.toNumberString(); + UnicodeString expectedString = expectedQuantity.toScientificString(); if (U_FAILURE(status)) { appendErrorMessage.append("[Error parsing decnumber] "); // If this happens, assume that tuple.output is exactly the same format as - // DecimalQuantity.toNumberString() + // DecimalQuantity.toScientificString() expectedString = tuple.output; status = U_ZERO_ERROR; } - UnicodeString actualString = result.getDecimalQuantity()->toNumberString(); + UnicodeString actualString = result.getDecimalQuantity()->toScientificString(); if (expectedString != actualString) { appendErrorMessage.append( UnicodeString("Expected: ") + tuple.output + " (i.e., " + expectedString + "), but got: " + @@ -503,7 +503,7 @@ UBool NumberFormatTestDataDriven::isParseCurrencyPass( } UnicodeString currStr(currAmt->getISOCurrency()); U_ASSERT(currAmt->getNumber().getDecimalQuantity() != nullptr); // no doubles in currency tests - UnicodeString resultStr = currAmt->getNumber().getDecimalQuantity()->toNumberString(); + UnicodeString resultStr = currAmt->getNumber().getDecimalQuantity()->toScientificString(); 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 @@ -511,7 +511,7 @@ UBool NumberFormatTestDataDriven::isParseCurrencyPass( DecimalQuantity expectedQuantity; strToDigitList(tuple.output, expectedQuantity, status); - UnicodeString expectedString = expectedQuantity.toNumberString(); + UnicodeString expectedString = expectedQuantity.toScientificString(); if (U_FAILURE(status)) { appendErrorMessage.append("Error parsing decnumber"); // If this happens, assume that tuple.output is exactly the same format as @@ -6978,15 +6978,11 @@ const char* attrString(int32_t attrId) { // API test, not a comprehensive test. // See DecimalFormatTest/DataDrivenTests // -#define ASSERT_SUCCESS(status) {if (U_FAILURE(status)) errln("file %s, line %d: status: %s", \ - __FILE__, __LINE__, u_errorName(status));} -#define ASSERT_EQUALS(expected, actual) {if ((expected) != (actual)) \ - errln("file %s, line %d: %s != %s", __FILE__, __LINE__, #expected, #actual);} - -static UBool operator != (const char *s1, UnicodeString &s2) { - // This function lets ASSERT_EQUALS("literal", UnicodeString) work. - UnicodeString us1(s1); - return us1 != s2; +#define ASSERT_SUCCESS(status) { \ + assertSuccess(UnicodeString("file ") + __FILE__ + ", line " + __LINE__, (status)); \ +} +#define ASSERT_EQUALS(expected, actual) { \ + assertEquals(UnicodeString("file ") + __FILE__ + ", line " + __LINE__, (expected), (actual)); \ } void NumberFormatTest::TestDecimal() { @@ -6996,7 +6992,7 @@ void NumberFormatTest::TestDecimal() { ASSERT_SUCCESS(status); StringPiece s = f.getDecimalNumber(status); ASSERT_SUCCESS(status); - ASSERT_EQUALS("1.2345678999987654321E+667", s); + ASSERT_EQUALS("1.2345678999987654321E+667", s.data()); //printf("%s\n", s.data()); } @@ -7015,7 +7011,7 @@ void NumberFormatTest::TestDecimal() { ASSERT_EQUALS(123.45, f.getDouble()); ASSERT_EQUALS(123.45, f.getDouble(status)); ASSERT_SUCCESS(status); - ASSERT_EQUALS("123.45", f.getDecimalNumber(status)); + ASSERT_EQUALS("123.45", f.getDecimalNumber(status).data()); ASSERT_SUCCESS(status); f.setDecimalNumber("4.5678E7", status); @@ -7030,7 +7026,7 @@ void NumberFormatTest::TestDecimal() { ASSERT_EQUALS(-123, f.getLong()); ASSERT_EQUALS(-123, f.getLong(status)); ASSERT_SUCCESS(status); - ASSERT_EQUALS("-123", f.getDecimalNumber(status)); + ASSERT_EQUALS("-123", f.getDecimalNumber(status).data()); ASSERT_SUCCESS(status); status = U_ZERO_ERROR; @@ -7040,7 +7036,7 @@ void NumberFormatTest::TestDecimal() { ASSERT_EQUALS(1234567890123LL, f.getInt64()); ASSERT_EQUALS(1234567890123LL, f.getInt64(status)); ASSERT_SUCCESS(status); - ASSERT_EQUALS("1234567890123", f.getDecimalNumber(status)); + ASSERT_EQUALS("1.234567890123E+12", f.getDecimalNumber(status).data()); ASSERT_SUCCESS(status); } @@ -7103,7 +7099,7 @@ void NumberFormatTest::TestDecimal() { Formattable result; fmtr->parse(input, result, status); ASSERT_SUCCESS(status); - ASSERT_EQUALS(0, strcmp("0.0184", result.getDecimalNumber(status).data())); + ASSERT_EQUALS("0.0184", result.getDecimalNumber(status).data()); //std::cout << result.getDecimalNumber(status).data(); delete fmtr; } @@ -7156,8 +7152,8 @@ void NumberFormatTest::TestCurrencyFractionDigits() { errln((UnicodeString)"NumberFormat::format() should return the same result - text1=" + text1 + " text2=" + text2); } - delete fmt; } + delete fmt; } void NumberFormatTest::TestExponentParse() { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java index 772691004ee..160c4fd1f80 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java @@ -947,7 +947,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity { if (isNegative()) { sb.append('-'); } - if (precision == 0) { + if (precision == 0 || getMagnitude() < 0) { sb.append('0'); } for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { @@ -958,6 +958,48 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity { return sb.toString(); } + public String toScientificString() { + StringBuilder sb = new StringBuilder(); + toScientificString(sb); + return sb.toString(); + } + + public void toScientificString(StringBuilder result) { + assert(!isApproximate); + if (isNegative()) { + result.append('-'); + } + if (precision == 0) { + result.append("0E+0"); + return; + } + result.append((char) ('0' + getDigitPos(precision - 1))); + if (precision > 1) { + result.append('.'); + for (int i = 1; i < precision; i++) { + result.append((char) ('0' + getDigitPos(precision - i - 1))); + } + } + result.append('E'); + int _scale = scale + precision - 1; + if (_scale < 0) { + _scale *= -1; + result.append('-'); + } else { + result.append('+'); + } + if (_scale == 0) { + result.append('0'); + } + int insertIndex = result.length(); + while (_scale > 0) { + int quot = _scale / 10; + int rem = _scale % 10; + result.insert(insertIndex, (char) ('0' + rem)); + _scale = quot; + } + } + /** * Returns a single digit from the BCD list. No internal state is changed by calling this method. * diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java index 72a923a2166..8d8e791ccaa 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java @@ -425,7 +425,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra toNumberString()); } - public String toNumberString() { + private String toNumberString() { StringBuilder sb = new StringBuilder(); if (usingBytes) { if (precision == 0) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java index 385944f677d..b3051bc4f68 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java @@ -306,6 +306,7 @@ final class NumberPropertyMapper { if (exportedProperties != null) { + exportedProperties.setCurrency(currency); exportedProperties.setMathContext(mathContext); exportedProperties.setRoundingMode(mathContext.getRoundingMode()); exportedProperties.setMinimumIntegerDigits(minInt); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java index f1ec3643378..fa54739c496 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java @@ -2006,7 +2006,7 @@ public class DecimalFormat extends NumberFormat { } /** - * Returns the user-specified currency. May be null. + * Returns the currency used to display currency amounts. May be null. * * @see #setCurrency * @see DecimalFormatSymbols#getCurrency @@ -2015,7 +2015,7 @@ public class DecimalFormat extends NumberFormat { */ @Override public synchronized Currency getCurrency() { - return properties.getCurrency(); + return exportedProperties.getCurrency(); } /** diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index d1386fcb87b..6a784fdf4c6 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -5450,8 +5450,8 @@ public class NumberFormatTest extends TestFmwk { @Test public void testGetSetCurrency() { - DecimalFormat df = new DecimalFormat("¤#"); - assertEquals("Currency should start out null", null, df.getCurrency()); + DecimalFormat df = new DecimalFormat("¤#", DecimalFormatSymbols.getInstance(ULocale.US)); + assertEquals("Currency should start out as the locale default", Currency.getInstance("USD"), df.getCurrency()); Currency curr = Currency.getInstance("EUR"); df.setCurrency(curr); assertEquals("Currency should equal EUR after set", curr, df.getCurrency()); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java index 85877343a25..256f548e846 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java @@ -292,17 +292,17 @@ public class DecimalQuantityTest extends TestFmwk { fq.setToLong(1234123412341234L); assertFalse("Should not be using byte array", fq.isUsingBytes()); - assertEquals("Failed on initialize", "1234123412341234E0", fq.toNumberString()); + assertEquals("Failed on initialize", "1.234123412341234E+15", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); // Long -> Bytes fq.appendDigit((byte) 5, 0, true); assertTrue("Should be using byte array", fq.isUsingBytes()); - assertEquals("Failed on multiply", "12341234123412345E0", fq.toNumberString()); + assertEquals("Failed on multiply", "1.2341234123412345E+16", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); // Bytes -> Long fq.roundToMagnitude(5, MATH_CONTEXT_HALF_EVEN); assertFalse("Should not be using byte array", fq.isUsingBytes()); - assertEquals("Failed on round", "123412341234E5", fq.toNumberString()); + assertEquals("Failed on round", "1.23412341234E+16", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); } @@ -310,48 +310,46 @@ public class DecimalQuantityTest extends TestFmwk { public void testAppend() { DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(); fq.appendDigit((byte) 1, 0, true); - assertEquals("Failed on append", "1E0", fq.toNumberString()); + assertEquals("Failed on append", "1E+0", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 2, 0, true); - assertEquals("Failed on append", "12E0", fq.toNumberString()); + assertEquals("Failed on append", "1.2E+1", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 3, 1, true); - assertEquals("Failed on append", "1203E0", fq.toNumberString()); + assertEquals("Failed on append", "1.203E+3", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 0, 1, true); - assertEquals("Failed on append", "1203E2", fq.toNumberString()); + assertEquals("Failed on append", "1.203E+5", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 4, 0, true); - assertEquals("Failed on append", "1203004E0", fq.toNumberString()); + assertEquals("Failed on append", "1.203004E+6", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 0, 0, true); - assertEquals("Failed on append", "1203004E1", fq.toNumberString()); + assertEquals("Failed on append", "1.203004E+7", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 5, 0, false); - assertEquals("Failed on append", "120300405E-1", fq.toNumberString()); + assertEquals("Failed on append", "1.20300405E+7", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 6, 0, false); - assertEquals("Failed on append", "1203004056E-2", fq.toNumberString()); + assertEquals("Failed on append", "1.203004056E+7", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); fq.appendDigit((byte) 7, 3, false); - assertEquals("Failed on append", "12030040560007E-6", fq.toNumberString()); + assertEquals("Failed on append", "1.2030040560007E+7", fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); - StringBuilder baseExpected = new StringBuilder("12030040560007"); + StringBuilder baseExpected = new StringBuilder("1.2030040560007"); for (int i = 0; i < 10; i++) { fq.appendDigit((byte) 8, 0, false); baseExpected.append('8'); StringBuilder expected = new StringBuilder(baseExpected); - expected.append("E"); - expected.append(-7 - i); - assertEquals("Failed on append", expected.toString(), fq.toNumberString()); + expected.append("E+7"); + assertEquals("Failed on append", expected.toString(), fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); } fq.appendDigit((byte) 9, 2, false); baseExpected.append("009"); StringBuilder expected = new StringBuilder(baseExpected); - expected.append('E'); - expected.append("-19"); - assertEquals("Failed on append", expected.toString(), fq.toNumberString()); + expected.append("E+7"); + assertEquals("Failed on append", expected.toString(), fq.toScientificString()); assertNull("Failed health check", fq.checkHealth()); }