ICU-13836 C++ port of adding exponent for better plurals for compact decimal format

This commit is contained in:
Elango Cheran 2020-01-24 18:12:17 -08:00 committed by Elango
parent d6b88d49e3
commit 1a9fb8ec33
12 changed files with 348 additions and 72 deletions

View File

@ -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();
}

View File

@ -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) {

View File

@ -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();

View File

@ -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();
}

View File

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

View File

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

View File

@ -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);

View File

@ -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"123456789", 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"1234567", 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"123456", 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"1000", 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 */

View File

@ -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)));
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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, "123456789", 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, "123456789", 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, "1234567", 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, "1234567", 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, "123456", 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, "123456", 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, "1000", 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, "1000", 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) {