ICU-13836 C++ port of adding exponent for better plurals for compact decimal format
This commit is contained in:
parent
d6b88d49e3
commit
1a9fb8ec33
@ -280,12 +280,13 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr
|
||||
|
||||
// Treat zero, NaN, and infinity as if they had magnitude 0
|
||||
int32_t magnitude;
|
||||
int32_t multiplier = 0;
|
||||
if (quantity.isZeroish()) {
|
||||
magnitude = 0;
|
||||
micros.rounder.apply(quantity, status);
|
||||
} else {
|
||||
// TODO: Revisit chooseMultiplierAndApply
|
||||
int32_t multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status);
|
||||
multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status);
|
||||
magnitude = quantity.isZeroish() ? 0 : quantity.getMagnitude();
|
||||
magnitude -= multiplier;
|
||||
}
|
||||
@ -322,6 +323,11 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr
|
||||
micros.modMiddle = unsafePatternModifier;
|
||||
}
|
||||
|
||||
// Change the exponent only after we select appropriate plural form
|
||||
// for formatting purposes so that we preserve expected formatted
|
||||
// string behavior.
|
||||
quantity.adjustExponent(-1 * multiplier);
|
||||
|
||||
// We already performed rounding. Do not perform it again.
|
||||
micros.rounder = RoundingImpl::passThrough();
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) {
|
||||
origDouble = other.origDouble;
|
||||
origDelta = other.origDelta;
|
||||
isApproximate = other.isApproximate;
|
||||
exponent = other.exponent;
|
||||
}
|
||||
|
||||
void DecimalQuantity::clear() {
|
||||
@ -269,11 +270,21 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const {
|
||||
return fractionCount();
|
||||
case PLURAL_OPERAND_W:
|
||||
return fractionCountWithoutTrailingZeros();
|
||||
case PLURAL_OPERAND_E:
|
||||
return static_cast<double>(getExponent());
|
||||
default:
|
||||
return std::abs(toDouble());
|
||||
}
|
||||
}
|
||||
|
||||
int32_t DecimalQuantity::getExponent() const {
|
||||
return exponent;
|
||||
}
|
||||
|
||||
void DecimalQuantity::adjustExponent(int delta) {
|
||||
exponent = exponent + delta;
|
||||
}
|
||||
|
||||
bool DecimalQuantity::hasIntegerValue() const {
|
||||
return scale >= 0;
|
||||
}
|
||||
@ -307,11 +318,13 @@ int8_t DecimalQuantity::getDigit(int32_t magnitude) const {
|
||||
}
|
||||
|
||||
int32_t DecimalQuantity::fractionCount() const {
|
||||
return -getLowerDisplayMagnitude();
|
||||
int32_t fractionCountWithExponent = -getLowerDisplayMagnitude() - exponent;
|
||||
return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0;
|
||||
}
|
||||
|
||||
int32_t DecimalQuantity::fractionCountWithoutTrailingZeros() const {
|
||||
return -scale > 0 ? -scale : 0; // max(-scale, 0)
|
||||
int32_t fractionCountWithExponent = -scale - exponent;
|
||||
return fractionCountWithExponent > 0 ? fractionCountWithExponent : 0; // max(-fractionCountWithExponent, 0)
|
||||
}
|
||||
|
||||
bool DecimalQuantity::isNegative() const {
|
||||
@ -525,12 +538,12 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
|
||||
// if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ }
|
||||
// Fallback behavior upon truncateIfOverflow is to truncate at 17 digits.
|
||||
uint64_t result = 0L;
|
||||
int32_t upperMagnitude = scale + precision - 1;
|
||||
int32_t upperMagnitude = exponent + scale + precision - 1;
|
||||
if (truncateIfOverflow) {
|
||||
upperMagnitude = std::min(upperMagnitude, 17);
|
||||
}
|
||||
for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) {
|
||||
result = result * 10 + getDigitPos(magnitude - scale);
|
||||
result = result * 10 + getDigitPos(magnitude - scale - exponent);
|
||||
}
|
||||
if (isNegative()) {
|
||||
return static_cast<int64_t>(0LL - result); // i.e., -result
|
||||
@ -540,7 +553,7 @@ int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const {
|
||||
|
||||
uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
|
||||
uint64_t result = 0L;
|
||||
int32_t magnitude = -1;
|
||||
int32_t magnitude = -1 - exponent;
|
||||
int32_t lowerMagnitude = scale;
|
||||
if (includeTrailingZeros) {
|
||||
lowerMagnitude = std::min(lowerMagnitude, rReqPos);
|
||||
@ -564,7 +577,7 @@ bool DecimalQuantity::fitsInLong(bool ignoreFraction) const {
|
||||
if (isZeroish()) {
|
||||
return true;
|
||||
}
|
||||
if (scale < 0 && !ignoreFraction) {
|
||||
if (exponent + scale < 0 && !ignoreFraction) {
|
||||
return false;
|
||||
}
|
||||
int magnitude = getMagnitude();
|
||||
@ -881,12 +894,30 @@ UnicodeString DecimalQuantity::toPlainString() const {
|
||||
if (isNegative()) {
|
||||
sb.append(u'-');
|
||||
}
|
||||
if (precision == 0 || getMagnitude() < 0) {
|
||||
if (precision == 0) {
|
||||
sb.append(u'0');
|
||||
return sb;
|
||||
}
|
||||
int32_t upper = scale + precision + exponent - 1;
|
||||
int32_t lower = scale + exponent;
|
||||
if (upper < lReqPos - 1) {
|
||||
upper = lReqPos - 1;
|
||||
}
|
||||
if (lower > rReqPos) {
|
||||
lower = rReqPos;
|
||||
}
|
||||
int32_t p = upper;
|
||||
if (p < 0) {
|
||||
sb.append(u'0');
|
||||
}
|
||||
for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
|
||||
if (m == -1) { sb.append(u'.'); }
|
||||
sb.append(getDigit(m) + u'0');
|
||||
for (; p >= 0; p--) {
|
||||
sb.append(u'0' + getDigitPos(p - scale - exponent));
|
||||
}
|
||||
if (lower < 0) {
|
||||
sb.append(u'.');
|
||||
}
|
||||
for(; p >= lower; p--) {
|
||||
sb.append(u'0' + getDigitPos(p - scale - exponent));
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
@ -912,7 +943,7 @@ UnicodeString DecimalQuantity::toScientificString() const {
|
||||
}
|
||||
}
|
||||
result.append(u'E');
|
||||
int32_t _scale = upperPos + scale;
|
||||
int32_t _scale = upperPos + scale + exponent;
|
||||
if (_scale == INT32_MIN) {
|
||||
result.append({u"-2147483648", -1});
|
||||
return result;
|
||||
@ -1025,6 +1056,7 @@ void DecimalQuantity::setBcdToZero() {
|
||||
isApproximate = false;
|
||||
origDouble = 0;
|
||||
origDelta = 0;
|
||||
exponent = 0;
|
||||
}
|
||||
|
||||
void DecimalQuantity::readIntToBcd(int32_t n) {
|
||||
|
@ -146,6 +146,26 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
||||
*/
|
||||
int32_t getMagnitude() const;
|
||||
|
||||
/**
|
||||
* @return The value of the (suppressed) exponent after the number has been
|
||||
* put into a notation with exponents (ex: compact, scientific). Ex: given
|
||||
* the number 1000 as "1K" / "1E3", the return value will be 3 (positive).
|
||||
*/
|
||||
int32_t getExponent() const;
|
||||
|
||||
/**
|
||||
* Adjusts the value for the (suppressed) exponent stored when using
|
||||
* notation with exponents (ex: compact, scientific).
|
||||
*
|
||||
* <p>Adjusting the exponent is decoupled from {@link #adjustMagnitude} in
|
||||
* order to allow flexibility for {@link StandardPlural} to be selected in
|
||||
* formatting (ex: for compact notation) either with or without the exponent
|
||||
* applied in the value of the number.
|
||||
* @param delta
|
||||
* The value to adjust the exponent by.
|
||||
*/
|
||||
void adjustExponent(int32_t delta);
|
||||
|
||||
/**
|
||||
* @return Whether the value represented by this {@link DecimalQuantity} is
|
||||
* zero, infinity, or NaN.
|
||||
@ -164,9 +184,19 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
||||
/** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */
|
||||
bool isNaN() const U_OVERRIDE;
|
||||
|
||||
/** @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. */
|
||||
/**
|
||||
* Note: this method incorporates the value of {@code exponent}
|
||||
* (for cases such as compact notation) to return the proper long value
|
||||
* represented by the result.
|
||||
* @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error.
|
||||
*/
|
||||
int64_t toLong(bool truncateIfOverflow = false) const;
|
||||
|
||||
/**
|
||||
* Note: this method incorporates the value of {@code exponent}
|
||||
* (for cases such as compact notation) to return the proper long value
|
||||
* represented by the result.
|
||||
*/
|
||||
uint64_t toFractionLong(bool includeTrailingZeros) const;
|
||||
|
||||
/**
|
||||
@ -351,6 +381,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
||||
int32_t lReqPos = 0;
|
||||
int32_t rReqPos = 0;
|
||||
|
||||
// The value of the (suppressed) exponent after the number has been put into
|
||||
// a notation with exponents (ex: compact, scientific).
|
||||
int32_t exponent = 0;
|
||||
|
||||
/**
|
||||
* The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map
|
||||
* to one digit. For example, the number "12345" in BCD is "0x12345".
|
||||
@ -423,7 +457,7 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
||||
|
||||
/**
|
||||
* Sets the internal representation to zero. Clears any values stored in scale, precision,
|
||||
* hasDouble, origDouble, origDelta, and BCD data.
|
||||
* hasDouble, origDouble, origDelta, exponent, and BCD data.
|
||||
*/
|
||||
void setBcdToZero();
|
||||
|
||||
|
@ -149,6 +149,11 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
|
||||
mod.set(exponent, this);
|
||||
micros.modInner = &mod;
|
||||
|
||||
// Change the exponent only after we select appropriate plural form
|
||||
// for formatting purposes so that we preserve expected formatted
|
||||
// string behavior.
|
||||
quantity.adjustExponent(exponent);
|
||||
|
||||
// We already performed rounding. Do not perform it again.
|
||||
micros.rounder = RoundingImpl::passThrough();
|
||||
}
|
||||
|
@ -1693,6 +1693,7 @@ double FixedDecimal::getPluralOperand(PluralOperand operand) const {
|
||||
case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits);
|
||||
case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
|
||||
case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
|
||||
case PLURAL_OPERAND_E: return 0;
|
||||
default:
|
||||
UPRV_UNREACHABLE; // unexpected.
|
||||
}
|
||||
|
@ -214,6 +214,12 @@ enum PluralOperand {
|
||||
*/
|
||||
PLURAL_OPERAND_W,
|
||||
|
||||
/**
|
||||
* Suppressed exponent for compact notation (exponent needed in
|
||||
* scientific notation with compact notation to approximate i).
|
||||
*/
|
||||
PLURAL_OPERAND_E,
|
||||
|
||||
/**
|
||||
* THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC.
|
||||
*
|
||||
|
@ -168,6 +168,8 @@ class DecimalQuantityTest : public IntlTest {
|
||||
void testToDouble();
|
||||
void testMaxDigits();
|
||||
void testNickelRounding();
|
||||
void testCompactDecimalSuppressedExponent();
|
||||
void testSuppressedExponentUnchangedByInitialScaling();
|
||||
|
||||
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
|
||||
|
||||
|
@ -30,6 +30,8 @@ void DecimalQuantityTest::runIndexedTest(int32_t index, UBool exec, const char *
|
||||
TESTCASE_AUTO(testToDouble);
|
||||
TESTCASE_AUTO(testMaxDigits);
|
||||
TESTCASE_AUTO(testNickelRounding);
|
||||
TESTCASE_AUTO(testCompactDecimalSuppressedExponent);
|
||||
TESTCASE_AUTO(testSuppressedExponentUnchangedByInitialScaling);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
@ -467,4 +469,191 @@ void DecimalQuantityTest::testNickelRounding() {
|
||||
status.expectErrorAndReset(U_FORMAT_INEXACT_ERROR);
|
||||
}
|
||||
|
||||
void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
|
||||
IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
|
||||
Locale ulocale("fr-FR");
|
||||
|
||||
struct TestCase {
|
||||
UnicodeString skeleton;
|
||||
double input;
|
||||
const char16_t* expectedString;
|
||||
int64_t expectedLong;
|
||||
double expectedDouble;
|
||||
const char16_t* expectedPlainString;
|
||||
int32_t expectedSuppressedExponent;
|
||||
} cases[] = {
|
||||
// unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent
|
||||
{u"", 123456789, u"123 456 789", 123456789L, 123456789.0, u"123456789", 0},
|
||||
{u"compact-long", 123456789, u"123 millions", 123000000L, 123000000.0, u"123000000", 6},
|
||||
{u"compact-short", 123456789, u"123 M", 123000000L, 123000000.0, u"123000000", 6},
|
||||
{u"scientific", 123456789, u"1,234568E8", 123456800L, 123456800.0, u"123456800", 8},
|
||||
|
||||
{u"", 1234567, u"1 234 567", 1234567L, 1234567.0, u"1234567", 0},
|
||||
{u"compact-long", 1234567, u"1,2 million", 1200000L, 1200000.0, u"1200000", 6},
|
||||
{u"compact-short", 1234567, u"1,2 M", 1200000L, 1200000.0, u"1200000", 6},
|
||||
{u"scientific", 1234567, u"1,234567E6", 1234567L, 1234567.0, u"1234567", 6},
|
||||
|
||||
{u"", 123456, u"123 456", 123456L, 123456.0, u"123456", 0},
|
||||
{u"compact-long", 123456, u"123 mille", 123000L, 123000.0, u"123000", 3},
|
||||
{u"compact-short", 123456, u"123 k", 123000L, 123000.0, u"123000", 3},
|
||||
{u"scientific", 123456, u"1,23456E5", 123456L, 123456.0, u"123456", 5},
|
||||
|
||||
{u"", 123, u"123", 123L, 123.0, u"123", 0},
|
||||
{u"compact-long", 123, u"123", 123L, 123.0, u"123", 0},
|
||||
{u"compact-short", 123, u"123", 123L, 123.0, u"123", 0},
|
||||
{u"scientific", 123, u"1,23E2", 123L, 123.0, u"123", 2},
|
||||
|
||||
{u"", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
|
||||
{u"compact-long", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
|
||||
{u"compact-short", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
|
||||
{u"scientific", 1.2, u"1,2E0", 1L, 1.2, u"1.2", 0},
|
||||
|
||||
{u"", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
|
||||
{u"compact-long", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
|
||||
{u"compact-short", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
|
||||
{u"scientific", 0.12, u"1,2E-1", 0L, 0.12, u"0.12", -1},
|
||||
|
||||
{u"", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
|
||||
{u"compact-long", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
|
||||
{u"compact-short", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
|
||||
{u"scientific", 0.012, u"1,2E-2", 0L, 0.012, u"0.012", -2},
|
||||
|
||||
{u"", 999.9, u"999,9", 999L, 999.9, u"999.9", 0},
|
||||
{u"compact-long", 999.9, u"1 millier", 1000L, 1000.0, u"1000", 3},
|
||||
{u"compact-short", 999.9, u"1 k", 1000L, 1000.0, u"1000", 3},
|
||||
{u"scientific", 999.9, u"9,999E2", 999L, 999.9, u"999.9", 2},
|
||||
|
||||
{u"", 1000.0, u"1 000", 1000L, 1000.0, u"1000", 0},
|
||||
{u"compact-long", 1000.0, u"1 millier", 1000L, 1000.0, u"1000", 3},
|
||||
{u"compact-short", 1000.0, u"1 k", 1000L, 1000.0, u"1000", 3},
|
||||
{u"scientific", 1000.0, u"1E3", 1000L, 1000.0, u"1000", 3},
|
||||
};
|
||||
for (const auto& cas : cases) {
|
||||
// test the helper methods used to compute plural operand values
|
||||
|
||||
LocalizedNumberFormatter formatter =
|
||||
NumberFormatter::forSkeleton(cas.skeleton, status)
|
||||
.locale(ulocale);
|
||||
FormattedNumber fn = formatter.formatDouble(cas.input, status);
|
||||
DecimalQuantity dq;
|
||||
fn.getDecimalQuantity(dq, status);
|
||||
UnicodeString actualString = fn.toString(status);
|
||||
int64_t actualLong = dq.toLong();
|
||||
double actualDouble = dq.toDouble();
|
||||
UnicodeString actualPlainString = dq.toPlainString();
|
||||
int32_t actualSuppressedExponent = dq.getExponent();
|
||||
|
||||
assertEquals(
|
||||
u"formatted number " + cas.skeleton + u" toString: " + cas.input,
|
||||
cas.expectedString,
|
||||
actualString);
|
||||
assertEquals(
|
||||
u"compact decimal " + cas.skeleton + u" toLong: " + cas.input,
|
||||
cas.expectedLong,
|
||||
actualLong);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + cas.skeleton + u" toDouble: " + cas.input,
|
||||
cas.expectedDouble,
|
||||
actualDouble);
|
||||
assertEquals(
|
||||
u"formatted number " + cas.skeleton + u" toPlainString: " + cas.input,
|
||||
cas.expectedPlainString,
|
||||
actualPlainString);
|
||||
assertEquals(
|
||||
u"compact decimal " + cas.skeleton + u" suppressed exponent: " + cas.input,
|
||||
cas.expectedSuppressedExponent,
|
||||
actualSuppressedExponent);
|
||||
|
||||
// test the actual computed values of the plural operands
|
||||
|
||||
double expectedNOperand = cas.expectedDouble;
|
||||
double expectedIOperand = cas.expectedLong;
|
||||
double expectedEOperand = cas.expectedSuppressedExponent;
|
||||
double actualNOperand = dq.getPluralOperand(PLURAL_OPERAND_N);
|
||||
double actualIOperand = dq.getPluralOperand(PLURAL_OPERAND_I);
|
||||
double actualEOperand = dq.getPluralOperand(PLURAL_OPERAND_E);
|
||||
|
||||
assertEquals(
|
||||
u"formatted number " + cas.skeleton + u" toString: " + cas.input,
|
||||
cas.expectedString,
|
||||
actualString);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + cas.skeleton + u" n operand: " + cas.input,
|
||||
expectedNOperand,
|
||||
actualNOperand);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + cas.skeleton + u" i operand: " + cas.input,
|
||||
expectedIOperand,
|
||||
actualIOperand);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + cas.skeleton + " e operand: " + cas.input,
|
||||
expectedEOperand,
|
||||
actualEOperand);
|
||||
}
|
||||
}
|
||||
|
||||
void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
|
||||
IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
|
||||
Locale ulocale("fr-FR");
|
||||
LocalizedNumberFormatter withLocale = NumberFormatter::withLocale(ulocale);
|
||||
LocalizedNumberFormatter compactLong =
|
||||
withLocale.notation(Notation::compactLong());
|
||||
LocalizedNumberFormatter compactScaled =
|
||||
compactLong.scale(Scale::powerOfTen(3));
|
||||
|
||||
struct TestCase {
|
||||
int32_t input;
|
||||
UnicodeString expectedString;
|
||||
double expectedNOperand;
|
||||
double expectedIOperand;
|
||||
double expectedEOperand;
|
||||
} cases[] = {
|
||||
// input, compact long string output,
|
||||
// compact n operand, compact i operand, compact e operand
|
||||
{123456789, "123 millions", 123000000.0, 123000000.0, 6.0},
|
||||
{1234567, "1,2 million", 1200000.0, 1200000.0, 6.0},
|
||||
{123456, "123 mille", 123000.0, 123000.0, 3.0},
|
||||
{123, "123", 123.0, 123.0, 0.0},
|
||||
};
|
||||
|
||||
for (const auto& cas : cases) {
|
||||
FormattedNumber fnCompactScaled = compactScaled.formatInt(cas.input, status);
|
||||
DecimalQuantity dqCompactScaled;
|
||||
fnCompactScaled.getDecimalQuantity(dqCompactScaled, status);
|
||||
double compactScaledEOperand = dqCompactScaled.getPluralOperand(PLURAL_OPERAND_E);
|
||||
|
||||
FormattedNumber fnCompact = compactLong.formatInt(cas.input, status);
|
||||
DecimalQuantity dqCompact;
|
||||
fnCompact.getDecimalQuantity(dqCompact, status);
|
||||
UnicodeString actualString = fnCompact.toString(status);
|
||||
double compactNOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_N);
|
||||
double compactIOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_I);
|
||||
double compactEOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_E);
|
||||
assertEquals(
|
||||
u"formatted number " + Int64ToUnicodeString(cas.input) + " compactLong toString: ",
|
||||
cas.expectedString,
|
||||
actualString);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + DoubleToUnicodeString(cas.input) + ", n operand vs. expected",
|
||||
cas.expectedNOperand,
|
||||
compactNOperand);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + DoubleToUnicodeString(cas.input) + ", i operand vs. expected",
|
||||
cas.expectedIOperand,
|
||||
compactIOperand);
|
||||
assertDoubleEquals(
|
||||
u"compact decimal " + DoubleToUnicodeString(cas.input) + ", e operand vs. expected",
|
||||
cas.expectedEOperand,
|
||||
compactEOperand);
|
||||
|
||||
// By scaling by 10^3 in a locale that has words / compact notation
|
||||
// based on powers of 10^3, we guarantee that the suppressed
|
||||
// exponent will differ by 3.
|
||||
assertDoubleEquals(
|
||||
u"decimal " + DoubleToUnicodeString(cas.input) + ", e operand for compact vs. compact scaled",
|
||||
compactEOperand + 3,
|
||||
compactScaledEOperand);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
@ -1040,10 +1040,15 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
}
|
||||
|
||||
int p = upper;
|
||||
if (p < 0) {
|
||||
result.append('0');
|
||||
}
|
||||
for (; p >= 0; p--) {
|
||||
result.append((char) ('0' + getDigitPos(p - scale - exponent)));
|
||||
}
|
||||
result.append('.');
|
||||
if (lower < 0) {
|
||||
result.append('.');
|
||||
}
|
||||
for(; p >= lower; p--) {
|
||||
result.append((char) ('0' + getDigitPos(p - scale - exponent)));
|
||||
}
|
||||
|
@ -549,8 +549,6 @@ public class PluralRules implements Serializable {
|
||||
|
||||
private final int baseFactor;
|
||||
|
||||
final int suppressedExponent;
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
@ -632,15 +630,6 @@ public class PluralRules implements Serializable {
|
||||
return baseFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getSuppressedExponent() {
|
||||
return suppressedExponent;
|
||||
}
|
||||
|
||||
static final long MAX = (long)1E18;
|
||||
|
||||
/**
|
||||
@ -661,7 +650,6 @@ public class PluralRules implements Serializable {
|
||||
? MAX
|
||||
: (long)n;
|
||||
hasIntegerValue = source == integerValue;
|
||||
suppressedExponent = 0;
|
||||
// check values. TODO make into unit test.
|
||||
//
|
||||
// long visiblePower = (int) Math.pow(10, v);
|
||||
@ -819,7 +807,7 @@ public class PluralRules implements Serializable {
|
||||
case t: return decimalDigitsWithoutTrailingZeros;
|
||||
case v: return visibleDecimalDigitCount;
|
||||
case w: return visibleDecimalDigitCountWithoutTrailingZeros;
|
||||
case e: return suppressedExponent;
|
||||
case e: return 0;
|
||||
default: return source;
|
||||
}
|
||||
}
|
||||
|
@ -898,9 +898,17 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
|
||||
if (isNegative()) {
|
||||
sb.append('-');
|
||||
}
|
||||
for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
|
||||
sb.append(getDigit(m));
|
||||
if (m == 0) sb.append('.');
|
||||
int upper = getUpperDisplayMagnitude();
|
||||
int lower = getLowerDisplayMagnitude();
|
||||
int p = upper;
|
||||
for (; p >= 0; p--) {
|
||||
sb.append((char) ('0' + getDigit(p)));
|
||||
}
|
||||
if (lower < 0) {
|
||||
sb.append('.');
|
||||
}
|
||||
for(; p >= lower; p--) {
|
||||
sb.append((char) ('0' + getDigit(p)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -535,13 +535,13 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
@Test
|
||||
public void testNickelRounding() {
|
||||
Object[][] cases = new Object[][] {
|
||||
{1.000, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.001, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.010, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.020, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.024, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.025, -2, RoundingMode.HALF_EVEN, "1."},
|
||||
{1.025, -2, RoundingMode.HALF_DOWN, "1."},
|
||||
{1.000, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.001, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.010, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.020, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.024, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.025, -2, RoundingMode.HALF_EVEN, "1"},
|
||||
{1.025, -2, RoundingMode.HALF_DOWN, "1"},
|
||||
{1.025, -2, RoundingMode.HALF_UP, "1.05"},
|
||||
{1.026, -2, RoundingMode.HALF_EVEN, "1.05"},
|
||||
{1.030, -2, RoundingMode.HALF_EVEN, "1.05"},
|
||||
@ -557,28 +557,28 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
{1.080, -2, RoundingMode.HALF_EVEN, "1.1"},
|
||||
{1.090, -2, RoundingMode.HALF_EVEN, "1.1"},
|
||||
{1.099, -2, RoundingMode.HALF_EVEN, "1.1"},
|
||||
{1.999, -2, RoundingMode.HALF_EVEN, "2."},
|
||||
{2.25, -1, RoundingMode.HALF_EVEN, "2."},
|
||||
{1.999, -2, RoundingMode.HALF_EVEN, "2"},
|
||||
{2.25, -1, RoundingMode.HALF_EVEN, "2"},
|
||||
{2.25, -1, RoundingMode.HALF_UP, "2.5"},
|
||||
{2.75, -1, RoundingMode.HALF_DOWN, "2.5"},
|
||||
{2.75, -1, RoundingMode.HALF_EVEN, "3."},
|
||||
{3.00, -1, RoundingMode.CEILING, "3."},
|
||||
{2.75, -1, RoundingMode.HALF_EVEN, "3"},
|
||||
{3.00, -1, RoundingMode.CEILING, "3"},
|
||||
{3.25, -1, RoundingMode.CEILING, "3.5"},
|
||||
{3.50, -1, RoundingMode.CEILING, "3.5"},
|
||||
{3.75, -1, RoundingMode.CEILING, "4."},
|
||||
{4.00, -1, RoundingMode.FLOOR, "4."},
|
||||
{4.25, -1, RoundingMode.FLOOR, "4."},
|
||||
{3.75, -1, RoundingMode.CEILING, "4"},
|
||||
{4.00, -1, RoundingMode.FLOOR, "4"},
|
||||
{4.25, -1, RoundingMode.FLOOR, "4"},
|
||||
{4.50, -1, RoundingMode.FLOOR, "4.5"},
|
||||
{4.75, -1, RoundingMode.FLOOR, "4.5"},
|
||||
{5.00, -1, RoundingMode.UP, "5."},
|
||||
{5.00, -1, RoundingMode.UP, "5"},
|
||||
{5.25, -1, RoundingMode.UP, "5.5"},
|
||||
{5.50, -1, RoundingMode.UP, "5.5"},
|
||||
{5.75, -1, RoundingMode.UP, "6."},
|
||||
{6.00, -1, RoundingMode.DOWN, "6."},
|
||||
{6.25, -1, RoundingMode.DOWN, "6."},
|
||||
{5.75, -1, RoundingMode.UP, "6"},
|
||||
{6.00, -1, RoundingMode.DOWN, "6"},
|
||||
{6.25, -1, RoundingMode.DOWN, "6"},
|
||||
{6.50, -1, RoundingMode.DOWN, "6.5"},
|
||||
{6.75, -1, RoundingMode.DOWN, "6.5"},
|
||||
{7.00, -1, RoundingMode.UNNECESSARY, "7."},
|
||||
{7.00, -1, RoundingMode.UNNECESSARY, "7"},
|
||||
{7.50, -1, RoundingMode.UNNECESSARY, "7.5"},
|
||||
};
|
||||
for (Object[] cas : cases) {
|
||||
@ -614,25 +614,25 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
|
||||
Object[][] casesData = {
|
||||
// unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent
|
||||
{"", 123456789, "123 456 789", 123456789L, 123456789.0, new BigDecimal("123456789"), "123456789.", 0},
|
||||
{"compact-long", 123456789, "123 millions", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000.", 6},
|
||||
{"compact-short", 123456789, "123 M", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000.", 6},
|
||||
{"scientific", 123456789, "1,234568E8", 123456800L, 123456800.0, new BigDecimal("123456800"), "123456800.", 8},
|
||||
{"", 123456789, "123 456 789", 123456789L, 123456789.0, new BigDecimal("123456789"), "123456789", 0},
|
||||
{"compact-long", 123456789, "123 millions", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6},
|
||||
{"compact-short", 123456789, "123 M", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6},
|
||||
{"scientific", 123456789, "1,234568E8", 123456800L, 123456800.0, new BigDecimal("123456800"), "123456800", 8},
|
||||
|
||||
{"", 1234567, "1 234 567", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567.", 0},
|
||||
{"compact-long", 1234567, "1,2 million", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000.", 6},
|
||||
{"compact-short", 1234567, "1,2 M", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000.", 6},
|
||||
{"scientific", 1234567, "1,234567E6", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567.", 6},
|
||||
{"", 1234567, "1 234 567", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 0},
|
||||
{"compact-long", 1234567, "1,2 million", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6},
|
||||
{"compact-short", 1234567, "1,2 M", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6},
|
||||
{"scientific", 1234567, "1,234567E6", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 6},
|
||||
|
||||
{"", 123456, "123 456", 123456L, 123456.0, new BigDecimal("123456"), "123456.", 0},
|
||||
{"compact-long", 123456, "123 mille", 123000L, 123000.0, new BigDecimal("123000"), "123000.", 3},
|
||||
{"compact-short", 123456, "123 k", 123000L, 123000.0, new BigDecimal("123000"), "123000.", 3},
|
||||
{"scientific", 123456, "1,23456E5", 123456L, 123456.0, new BigDecimal("123456"), "123456.", 5},
|
||||
{"", 123456, "123 456", 123456L, 123456.0, new BigDecimal("123456"), "123456", 0},
|
||||
{"compact-long", 123456, "123 mille", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3},
|
||||
{"compact-short", 123456, "123 k", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3},
|
||||
{"scientific", 123456, "1,23456E5", 123456L, 123456.0, new BigDecimal("123456"), "123456", 5},
|
||||
|
||||
{"", 123, "123", 123L, 123.0, new BigDecimal("123"), "123.", 0},
|
||||
{"compact-long", 123, "123", 123L, 123.0, new BigDecimal("123"), "123.", 0},
|
||||
{"compact-short", 123, "123", 123L, 123.0, new BigDecimal("123"), "123.", 0},
|
||||
{"scientific", 123, "1,23E2", 123L, 123.0, new BigDecimal("123"), "123.", 2},
|
||||
{"", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
|
||||
{"compact-long", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
|
||||
{"compact-short", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
|
||||
{"scientific", 123, "1,23E2", 123L, 123.0, new BigDecimal("123"), "123", 2},
|
||||
|
||||
{"", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
|
||||
{"compact-long", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
|
||||
@ -650,14 +650,14 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
{"scientific", 0.012, "1,2E-2", 0L, 0.012, new BigDecimal("0.012"), "0.012", -2},
|
||||
|
||||
{"", 999.9, "999,9", 999L, 999.9, new BigDecimal("999.9"), "999.9", 0},
|
||||
{"compact-long", 999.9, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 3},
|
||||
{"compact-short", 999.9, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 3},
|
||||
{"compact-long", 999.9, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
|
||||
{"compact-short", 999.9, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
|
||||
{"scientific", 999.9, "9,999E2", 999L, 999.9, new BigDecimal("999.9"), "999.9", 2},
|
||||
|
||||
{"", 1000.0, "1 000", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 0},
|
||||
{"compact-long", 1000.0, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 3},
|
||||
{"compact-short", 1000.0, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 3},
|
||||
{"scientific", 1000.0, "1E3", 1000L, 1000.0, new BigDecimal("1000"), "1000.", 3},
|
||||
{"", 1000.0, "1 000", 1000L, 1000.0, new BigDecimal("1000"), "1000", 0},
|
||||
{"compact-long", 1000.0, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
|
||||
{"compact-short", 1000.0, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
|
||||
{"scientific", 1000.0, "1E3", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
|
||||
};
|
||||
|
||||
for (Object[] caseDatum : casesData) {
|
||||
|
Loading…
Reference in New Issue
Block a user