ICU-10273 add DecimalFormat::getFixedDecimal(Formattable &)

X-SVN-Rev: 34267
This commit is contained in:
Andy Heninger 2013-09-11 01:17:28 +00:00
parent e50e12a1dd
commit 90522bf7b1
4 changed files with 154 additions and 1 deletions

View file

@ -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&

View file

@ -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:
/**

View file

@ -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.

View file

@ -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 */