mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 00:43:32 +00:00
ICU-10273 add DecimalFormat::getFixedDecimal(Formattable &)
X-SVN-Rev: 34267
This commit is contained in:
parent
e50e12a1dd
commit
90522bf7b1
4 changed files with 154 additions and 1 deletions
|
@ -1073,6 +1073,73 @@ DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const {
|
|||
}
|
||||
|
||||
|
||||
FixedDecimal
|
||||
DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return FixedDecimal();
|
||||
}
|
||||
if (!number.isNumeric()) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return FixedDecimal();
|
||||
}
|
||||
|
||||
DigitList *digits = number.getDigitList();
|
||||
if (digits == NULL || digits->getCount() <= 15) {
|
||||
return getFixedDecimal(number.getDouble(), status);
|
||||
}
|
||||
|
||||
// We have an incoming DigitList in the formattable, and it holds more digits than
|
||||
// a double can safely represent.
|
||||
// Compute the fields of the fixed decimal directly from the digit list.
|
||||
|
||||
FixedDecimal result;
|
||||
result.source = digits->getDouble();
|
||||
|
||||
// Round the number according to the requirements of this Format.
|
||||
DigitList roundedNum;
|
||||
_round(*digits, roundedNum, result.isNegative, status);
|
||||
|
||||
// The int64_t fields in FixedDecimal can easily overflow.
|
||||
// In deciding what to discard in this event, consider that fixedDecimal
|
||||
// is being used only with PluralRules, and those rules mostly look at least significant
|
||||
// few digits of the integer part, and whether the fraction part is zero or not.
|
||||
//
|
||||
// So, in case of overflow when filling in the fields of the FixedDecimal object,
|
||||
// for the integer part, discard the most significant digits.
|
||||
// for the fraction part, discard the least significant digits,
|
||||
// don't truncate the fraction value to zero.
|
||||
// For simplicity, the int64_t fields are limited to 18 decimal digits, even
|
||||
// though they could hold most (but not all) 19 digit values.
|
||||
|
||||
// Integer Digits.
|
||||
int32_t di = roundedNum.getDecimalAt()-18; // Take at most 18 digits.
|
||||
if (di < 0) {
|
||||
di = 0;
|
||||
}
|
||||
result.intValue = 0;
|
||||
for (; di<roundedNum.getDecimalAt(); di++) {
|
||||
result.intValue = result.intValue * 10 + (roundedNum.getDigit(di) & 0x0f);
|
||||
}
|
||||
|
||||
// Fraction digits.
|
||||
result.visibleDecimalDigitCount = result.decimalDigits = result.decimalDigitsWithoutTrailingZeros = 0;
|
||||
for (di = roundedNum.getDecimalAt(); di < roundedNum.getCount(); di++) {
|
||||
result.visibleDecimalDigitCount++;
|
||||
if (result.decimalDigits < 100000000000000000LL) {
|
||||
// 9223372036854775807 Largest 64 bit signed integer
|
||||
int32_t digitVal = roundedNum.getDigit(di) & 0x0f; // getDigit() returns a char, '0'-'9'.
|
||||
result.decimalDigits = result.decimalDigits * 10 + digitVal;
|
||||
if (digitVal > 0) {
|
||||
result.decimalDigitsWithoutTrailingZeros = result.decimalDigits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.hasIntegerValue = (result.decimalDigits == 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
UnicodeString&
|
||||
|
|
|
@ -1861,6 +1861,14 @@ public:
|
|||
*/
|
||||
FixedDecimal getFixedDecimal(double number, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Get a FixedDecimal corresponding to a formattable as it would be
|
||||
* formatted by this DecimalFormat.
|
||||
* Internal, not intended for public use.
|
||||
* @internal
|
||||
*/
|
||||
FixedDecimal getFixedDecimal(const Formattable &number, UErrorCode &status) const;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
* Creates a Formattable object of an appropriate numeric type from a
|
||||
* a decimal number in string form. The Formattable will retain the
|
||||
* full precision of the input in decimal format, even when it exceeds
|
||||
* what can be represented by a double of int64_t.
|
||||
* what can be represented by a double or int64_t.
|
||||
*
|
||||
* @param number the unformatted (not localized) string representation
|
||||
* of the Decimal number.
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
#include "unicode/currpinf.h"
|
||||
#include "unicode/dcfmtsym.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/fmtable.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/parseerr.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
|
||||
#include "putilimp.h"
|
||||
#include "plurrule_impl.h"
|
||||
|
@ -666,6 +668,82 @@ void IntlTestDecimalFormatAPI::TestFixedDecimal() {
|
|||
fd = df->getFixedDecimal(uprv_getNaN(), status);
|
||||
ASSERT_EQUAL(TRUE, fd.isNanOrInfinity);
|
||||
ASSERT_SUCCESS(status);
|
||||
|
||||
// Test Big Decimal input.
|
||||
// 22 digits before and after decimal, will exceed the precision of a double
|
||||
// and force DecimalFormat::getFixedDecimal() to work with a digit list.
|
||||
df.adoptInstead(new DecimalFormat("#####################0.00####################", status));
|
||||
ASSERT_SUCCESS(status);
|
||||
Formattable fable("12.34", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(34, fd.decimalDigits);
|
||||
ASSERT_EQUAL(34, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(12, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
fable.setDecimalNumber("12.345678901234567890123456789", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(22, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(345678901234567890LL, fd.decimalDigits);
|
||||
ASSERT_EQUAL(34567890123456789LL, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(12, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
// On field overflow, Integer part is truncated on the left, fraction part on the right.
|
||||
fable.setDecimalNumber("123456789012345678901234567890.123456789012345678901234567890", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(22, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(123456789012345678LL, fd.decimalDigits);
|
||||
ASSERT_EQUAL(123456789012345678LL, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(345678901234567890LL, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
// Digits way to the right of the decimal but within the format's precision aren't truncated
|
||||
fable.setDecimalNumber("1.0000000000000000000012", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(22, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(12, fd.decimalDigits);
|
||||
ASSERT_EQUAL(12, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(1, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
// Digits beyond the precision of the format are rounded away
|
||||
fable.setDecimalNumber("1.000000000000000000000012", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(0, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(0, fd.decimalDigits);
|
||||
ASSERT_EQUAL(0, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(1, fd.intValue);
|
||||
ASSERT_EQUAL(TRUE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(FALSE, fd.isNegative);
|
||||
|
||||
// Negative numbers come through
|
||||
fable.setDecimalNumber("-1.0000000000000000000012", status);
|
||||
ASSERT_SUCCESS(status);
|
||||
fd = df->getFixedDecimal(fable, status);
|
||||
ASSERT_SUCCESS(status);
|
||||
ASSERT_EQUAL(22, fd.visibleDecimalDigitCount);
|
||||
ASSERT_EQUAL(12, fd.decimalDigits);
|
||||
ASSERT_EQUAL(12, fd.decimalDigitsWithoutTrailingZeros);
|
||||
ASSERT_EQUAL(1, fd.intValue);
|
||||
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
|
||||
ASSERT_EQUAL(TRUE, fd.isNegative);
|
||||
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
Loading…
Add table
Reference in a new issue