ICU-10273 Plural Rules, add DecimalFormat::getFixedDecimal(). Work in progress.

X-SVN-Rev: 34204
This commit is contained in:
Andy Heninger 2013-09-05 23:28:28 +00:00
parent bcfcd53be9
commit 7b3b75a525
5 changed files with 103 additions and 14 deletions

View file

@ -1022,19 +1022,42 @@ DecimalFormat::clone() const
FixedDecimal
DecimalFormat::getFixedDecimal(double number, UErrorCode &status) {
DigitList digits;
digits.set(number);
UBool isNegative;
_round(digits, digits, isNegative, status);
double roundedNum = digits.getDouble();
FixedDecimal result(roundedNum);
int32_t numTrailingFractionZeros = this->getMinimumFractionDigits() - result.visibleDecimalDigitCount;
DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const {
FixedDecimal result;
int32_t minFractionDigits = getMinimumFractionDigits();
if (fMultiplier == NULL && fScale == 0 && fRoundingIncrement == 0 && areSignificantDigitsUsed() == FALSE &&
result.quickInit(number) && result.visibleDecimalDigitCount <= getMaximumFractionDigits()) {
// Fast Path. Construction of an exact FixedDecimal directly from the double, without passing
// through a DigitList, was successful, and the formatter is doing nothing tricky with rounding.
// printf("getFixedDecimal(%g): taking fast path.\n", number);
} else {
// Slow path. Create a DigitList, and have this formatter round it according to the
// requirements of the format, and fill the fixedDecimal from that.
DigitList digits;
digits.set(number);
UBool isNegative;
_round(digits, digits, isNegative, status);
double roundedNum = digits.getDouble();
result.init(roundedNum);
if (areSignificantDigitsUsed()) {
minFractionDigits = getMinimumSignificantDigits() - digits.getDecimalAt();
if (minFractionDigits < 0) {
minFractionDigits = 0;
}
}
}
// Adjust result for trailing zeros to the right of the decimal. Needed for both fast & slow paths.
int32_t numTrailingFractionZeros = minFractionDigits - result.visibleDecimalDigitCount;
if (numTrailingFractionZeros > 0) {
double scaleFactor = pow(10.0, numTrailingFractionZeros);
result.decimalDigits *= scaleFactor;
result.visibleDecimalDigitCount += numTrailingFractionZeros;
}
return result;
}

View file

@ -1330,10 +1330,14 @@ FixedDecimal::FixedDecimal(double n, int32_t v) {
}
FixedDecimal::FixedDecimal(double n) {
int64_t numFractionDigits = decimals(n);
init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
init(n);
}
FixedDecimal::FixedDecimal() {
init(0, 0, 0);
}
// Create a FixedDecimal from a UnicodeString containing a number.
// Inefficient, but only used for samples, so simplicity trumps efficiency.
@ -1369,6 +1373,12 @@ FixedDecimal::FixedDecimal(const FixedDecimal &other) {
}
void FixedDecimal::init(double n) {
int64_t numFractionDigits = decimals(n);
init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
}
void FixedDecimal::init(double n, int32_t v, int64_t f) {
isNegative = n < 0;
source = fabs(n);
@ -1390,17 +1400,43 @@ void FixedDecimal::init(double n, int32_t v, int64_t f) {
}
}
// Fast path only exact initialization. Return true if successful.
// Note: Do not multiply by 10 each time through loop, rounding cruft can build
// up that makes the check for an integer result fail.
// A single multiply of the original number works more reliably.
static int p10[] = {1, 10, 100, 1000, 10000};
UBool FixedDecimal::quickInit(double n) {
UBool success = FALSE;
n = fabs(n);
int32_t numFractionDigits;
for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) {
double scaledN = n * p10[numFractionDigits];
if (scaledN == floor(scaledN)) {
success = TRUE;
break;
}
}
if (success) {
init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits));
}
return success;
}
int32_t FixedDecimal::decimals(double n) {
// Count the number of decimal digits in the fraction part of the number, excluding trailing zeros.
// fastpath the common cases, integers or fractions with 3 or fewer digits
n = fabs(n);
double scaledN = n;
for (int ndigits=0; ndigits<=3; ndigits++) {
// fastpath the common cases, integers or fractions with 3 or fewer digits
double scaledN = n * p10[ndigits];
if (scaledN == floor(scaledN)) {
return ndigits;
}
scaledN *= 10;
}
// Slow path, convert with sprintf, parse converted output.
char buf[30] = {0};
sprintf(buf, "%1.15e", n);
// formatted number looks like this: 1.234567890123457e-01

View file

@ -181,6 +181,7 @@ class U_I18N_API FixedDecimal: public UMemory {
FixedDecimal(double n, int32_t v, int64_t f);
FixedDecimal(double n, int32_t);
explicit FixedDecimal(double n);
FixedDecimal();
FixedDecimal(const UnicodeString &s, UErrorCode &ec);
FixedDecimal(const FixedDecimal &other);
@ -188,6 +189,9 @@ class U_I18N_API FixedDecimal: public UMemory {
int32_t getVisibleFractionDigitCount() const;
void init(double n, int32_t v, int64_t f);
void init(double n);
UBool quickInit(double n); // Try a fast-path only initialization,
// return TRUE if successful.
static int64_t getFractionalDigits(double n, int32_t v);
static int32_t decimals(double n);

View file

@ -1859,7 +1859,7 @@ public:
* Internal, not intended for public use.
* @internal
*/
FixedDecimal getFixedDecimal(double number, UErrorCode &status);
FixedDecimal getFixedDecimal(double number, UErrorCode &status) const;
public:

View file

@ -634,6 +634,32 @@ void IntlTestDecimalFormatAPI::TestFixedDecimal() {
ASSERT_EQUAL(123, fd.intValue);
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
ASSERT_EQUAL(FALSE, fd.isNegative);
df.adoptInstead(new DecimalFormat("@@@@@", status)); // Significant Digits
ASSERT_SUCCESS(status);
fd = df->getFixedDecimal(123, status);
ASSERT_SUCCESS(status);
ASSERT_EQUAL(2, fd.visibleDecimalDigitCount);
ASSERT_EQUAL(0, fd.decimalDigits);
ASSERT_EQUAL(0, fd.decimalDigitsWithoutTrailingZeros);
ASSERT_EQUAL(123, fd.intValue);
ASSERT_EQUAL(TRUE, fd.hasIntegerValue);
ASSERT_EQUAL(FALSE, fd.isNegative);
df.adoptInstead(new DecimalFormat("@@@@@", status)); // Significant Digits
ASSERT_SUCCESS(status);
fd = df->getFixedDecimal(1.23, status);
ASSERT_SUCCESS(status);
ASSERT_EQUAL(4, fd.visibleDecimalDigitCount);
ASSERT_EQUAL(2300, fd.decimalDigits);
ASSERT_EQUAL(23, fd.decimalDigitsWithoutTrailingZeros);
ASSERT_EQUAL(1, fd.intValue);
ASSERT_EQUAL(FALSE, fd.hasIntegerValue);
ASSERT_EQUAL(FALSE, fd.isNegative);
}
#endif /* #if !UCONFIG_NO_FORMATTING */