mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-09 15:27:38 +00:00
ICU-13634 Changing DecimalQuantity#toNumberString() to be DecimalQuantity#toScientificString() with slightly friendlier output syntax for better compatibility. More currency tweaks.
X-SVN-Rev: 41215
This commit is contained in:
parent
a901b5c04a
commit
cd92fa2c88
18 changed files with 194 additions and 132 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<const uint16_t*>(numberString.getBuffer()), numberString.length(), &count);
|
||||
if (isNegative()) {
|
||||
result = -result;
|
||||
}
|
||||
return result;
|
||||
return converter.StringToDouble(
|
||||
reinterpret_cast<const uint16_t*>(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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <UVector32> uvec(new UVector32(status));
|
||||
LocalPointer <UVector32> uvec(new UVector32(status), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check for invalid syntax and set the corresponding error code.
|
||||
if ((fContext.status & DEC_Conversion_syntax) != 0) {
|
||||
status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
|
||||
} else if (fContext.status != 0) {
|
||||
// Not a syntax error, but some other error, like an exponent that is too large.
|
||||
// For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
|
||||
if (decNumberIsSpecial(fData.getAlias())) {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -306,6 +306,7 @@ final class NumberPropertyMapper {
|
|||
|
||||
if (exportedProperties != null) {
|
||||
|
||||
exportedProperties.setCurrency(currency);
|
||||
exportedProperties.setMathContext(mathContext);
|
||||
exportedProperties.setRoundingMode(mathContext.getRoundingMode());
|
||||
exportedProperties.setMinimumIntegerDigits(minInt);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue