mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 17:24:01 +00:00
parent
fcca13200f
commit
4059be5964
14 changed files with 207 additions and 100 deletions
|
@ -181,20 +181,22 @@ uint64_t DecimalQuantity::getPositionFingerprint() const {
|
|||
return fingerprint;
|
||||
}
|
||||
|
||||
void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode roundingMode,
|
||||
UErrorCode& status) {
|
||||
void DecimalQuantity::roundToIncrement(
|
||||
uint64_t increment,
|
||||
digits_t magnitude,
|
||||
RoundingMode roundingMode,
|
||||
UErrorCode& status) {
|
||||
// Do not call this method with an increment having only a 1 or a 5 digit!
|
||||
// Use a more efficient call to either roundToMagnitude() or roundToNickel().
|
||||
// Check a few popular rounding increments; a more thorough check is in Java.
|
||||
U_ASSERT(roundingIncrement != 0.01);
|
||||
U_ASSERT(roundingIncrement != 0.05);
|
||||
U_ASSERT(roundingIncrement != 0.1);
|
||||
U_ASSERT(roundingIncrement != 0.5);
|
||||
U_ASSERT(roundingIncrement != 1);
|
||||
U_ASSERT(roundingIncrement != 5);
|
||||
U_ASSERT(increment != 1);
|
||||
U_ASSERT(increment != 5);
|
||||
|
||||
DecimalQuantity incrementDQ;
|
||||
incrementDQ.setToLong(increment);
|
||||
incrementDQ.adjustMagnitude(magnitude);
|
||||
DecNum incrementDN;
|
||||
incrementDN.setTo(roundingIncrement, status);
|
||||
incrementDQ.toDecNum(incrementDN, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Divide this DecimalQuantity by the increment, round, then multiply back.
|
||||
|
@ -254,6 +256,12 @@ bool DecimalQuantity::adjustMagnitude(int32_t delta) {
|
|||
return false;
|
||||
}
|
||||
|
||||
int32_t DecimalQuantity::adjustToZeroScale() {
|
||||
int32_t retval = scale;
|
||||
scale = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
double DecimalQuantity::getPluralOperand(PluralOperand operand) const {
|
||||
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
|
||||
// See the comment at the top of this file explaining the "isApproximate" field.
|
||||
|
|
|
@ -81,11 +81,15 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
|||
*
|
||||
* <p>If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead.
|
||||
*
|
||||
* @param roundingIncrement The increment to which to round.
|
||||
* @param increment The increment to which to round.
|
||||
* @param magnitude The power of 10 to which to round.
|
||||
* @param roundingMode The {@link RoundingMode} to use if rounding is necessary.
|
||||
*/
|
||||
void roundToIncrement(double roundingIncrement, RoundingMode roundingMode,
|
||||
UErrorCode& status);
|
||||
void roundToIncrement(
|
||||
uint64_t increment,
|
||||
digits_t magnitude,
|
||||
RoundingMode roundingMode,
|
||||
UErrorCode& status);
|
||||
|
||||
/** Removes all fraction digits. */
|
||||
void truncate();
|
||||
|
@ -140,6 +144,13 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
|||
*/
|
||||
bool adjustMagnitude(int32_t delta);
|
||||
|
||||
/**
|
||||
* Scales the number such that the least significant nonzero digit is at magnitude 0.
|
||||
*
|
||||
* @return The previous magnitude of the least significant digit.
|
||||
*/
|
||||
int32_t adjustToZeroScale();
|
||||
|
||||
/**
|
||||
* @return The power of ten corresponding to the most significant nonzero digit.
|
||||
* The number must not be zero.
|
||||
|
|
|
@ -134,7 +134,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
|||
if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) {
|
||||
precision = Precision::constructFraction(minFrac, maxFrac);
|
||||
} else {
|
||||
precision = Precision::constructIncrement(roundingIncrement, minFrac);
|
||||
// Convert the double increment to an integer increment
|
||||
precision = Precision::increment(roundingIncrement).withMinFraction(minFrac);
|
||||
}
|
||||
} else if (explicitMinMaxSig) {
|
||||
minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig;
|
||||
|
@ -293,9 +294,14 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
|||
} else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT
|
||||
|| rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE
|
||||
|| rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) {
|
||||
increment_ = rounding_.fUnion.increment.fIncrement;
|
||||
minFrac_ = rounding_.fUnion.increment.fMinFrac;
|
||||
// If incrementRounding is used, maxFrac is set equal to minFrac
|
||||
maxFrac_ = rounding_.fUnion.increment.fMinFrac;
|
||||
// Convert the integer increment to a double
|
||||
DecimalQuantity dq;
|
||||
dq.setToLong(rounding_.fUnion.increment.fIncrement);
|
||||
dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude);
|
||||
increment_ = dq.toDouble();
|
||||
} else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) {
|
||||
minSig_ = rounding_.fUnion.fracSig.fMinSig;
|
||||
maxSig_ = rounding_.fUnion.fracSig.fMaxSig;
|
||||
|
|
|
@ -750,7 +750,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
|
|||
int32_t groupingLength = grouping1 + grouping2 + 1;
|
||||
|
||||
// Figure out the digits we need to put in the pattern.
|
||||
double roundingInterval = properties.roundingIncrement;
|
||||
double increment = properties.roundingIncrement;
|
||||
UnicodeString digitsString;
|
||||
int32_t digitsStringScale = 0;
|
||||
if (maxSig != uprv_min(dosMax, -1)) {
|
||||
|
@ -761,14 +761,14 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
|
|||
while (digitsString.length() < maxSig) {
|
||||
digitsString.append(u'#');
|
||||
}
|
||||
} else if (roundingInterval != 0.0 && !ignoreRoundingIncrement(roundingInterval,maxFrac)) {
|
||||
// Rounding Interval.
|
||||
digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval, nullptr);
|
||||
// TODO: Check for DoS here?
|
||||
} else if (increment != 0.0 && !ignoreRoundingIncrement(increment,maxFrac)) {
|
||||
// Rounding Increment.
|
||||
DecimalQuantity incrementQuantity;
|
||||
incrementQuantity.setToDouble(roundingInterval);
|
||||
incrementQuantity.setToDouble(increment);
|
||||
incrementQuantity.roundToInfinity();
|
||||
digitsStringScale = incrementQuantity.getLowerDisplayMagnitude();
|
||||
incrementQuantity.adjustMagnitude(-digitsStringScale);
|
||||
incrementQuantity.roundToMagnitude(0, kDefaultMode, status);
|
||||
incrementQuantity.setMinInteger(minInt - digitsStringScale);
|
||||
UnicodeString str = incrementQuantity.toPlainString();
|
||||
if (str.charAt(0) == u'-') {
|
||||
// TODO: Unsupported operation exception or fail silently?
|
||||
|
|
|
@ -36,27 +36,24 @@ void number::impl::parseIncrementOption(const StringSegment &segment,
|
|||
// Utilize DecimalQuantity/decNumber to parse this for us.
|
||||
DecimalQuantity dq;
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
DecNum decnum;
|
||||
decnum.setTo({buffer.data(), buffer.length()}, localStatus);
|
||||
dq.setToDecNum(decnum, localStatus);
|
||||
if (U_FAILURE(localStatus) || decnum.isSpecial()) {
|
||||
dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
|
||||
if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) {
|
||||
// throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return;
|
||||
}
|
||||
double increment = dq.toDouble();
|
||||
|
||||
// We also need to figure out how many digits. Do a brute force string operation.
|
||||
int decimalOffset = 0;
|
||||
while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') {
|
||||
decimalOffset++;
|
||||
}
|
||||
if (decimalOffset == segment.length()) {
|
||||
outPrecision = Precision::increment(increment);
|
||||
} else {
|
||||
int32_t fractionLength = segment.length() - decimalOffset - 1;
|
||||
outPrecision = Precision::increment(increment).withMinFraction(fractionLength);
|
||||
// Now we break apart the number into a mantissa and exponent (magnitude).
|
||||
int32_t magnitude = dq.adjustToZeroScale();
|
||||
// setToDecNumber drops trailing zeros, so we search for the '.' manually.
|
||||
for (int32_t i=0; i<buffer.length(); i++) {
|
||||
if (buffer[i] == '.') {
|
||||
int32_t newMagnitude = i - buffer.length() + 1;
|
||||
dq.adjustMagnitude(magnitude - newMagnitude);
|
||||
magnitude = newMagnitude;
|
||||
break;
|
||||
}
|
||||
}
|
||||
outPrecision = Precision::incrementExact(dq.toLong(), magnitude);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -94,34 +91,6 @@ int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig)
|
|||
MultiplierProducer::~MultiplierProducer() = default;
|
||||
|
||||
|
||||
digits_t roundingutils::doubleFractionLength(double input, int8_t* singleDigit) {
|
||||
char buffer[DoubleToStringConverter::kBase10MaximalLength + 1];
|
||||
bool sign; // unused; always positive
|
||||
int32_t length;
|
||||
int32_t point;
|
||||
DoubleToStringConverter::DoubleToAscii(
|
||||
input,
|
||||
DoubleToStringConverter::DtoaMode::SHORTEST,
|
||||
0,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
&sign,
|
||||
&length,
|
||||
&point
|
||||
);
|
||||
|
||||
if (singleDigit == nullptr) {
|
||||
// no-op
|
||||
} else if (length == 1) {
|
||||
*singleDigit = buffer[0] - '0';
|
||||
} else {
|
||||
*singleDigit = -1;
|
||||
}
|
||||
|
||||
return static_cast<digits_t>(length - point);
|
||||
}
|
||||
|
||||
|
||||
Precision Precision::unlimited() {
|
||||
return Precision(RND_NONE, {});
|
||||
}
|
||||
|
@ -204,7 +173,19 @@ Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZero
|
|||
|
||||
IncrementPrecision Precision::increment(double roundingIncrement) {
|
||||
if (roundingIncrement > 0.0) {
|
||||
return constructIncrement(roundingIncrement, 0);
|
||||
DecimalQuantity dq;
|
||||
dq.setToDouble(roundingIncrement);
|
||||
dq.roundToInfinity();
|
||||
int32_t magnitude = dq.adjustToZeroScale();
|
||||
return constructIncrement(dq.toLong(), magnitude);
|
||||
} else {
|
||||
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
|
||||
}
|
||||
}
|
||||
|
||||
IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) {
|
||||
if (mantissa > 0.0) {
|
||||
return constructIncrement(mantissa, magnitude);
|
||||
} else {
|
||||
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
|
||||
}
|
||||
|
@ -269,8 +250,8 @@ Precision Precision::withCurrency(const CurrencyUnit ¤cy, UErrorCode &stat
|
|||
int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
|
||||
isoCode, fUnion.currencyUsage, &status);
|
||||
Precision retval = (increment != 0.0)
|
||||
? static_cast<Precision>(constructIncrement(increment, minMaxFrac))
|
||||
: static_cast<Precision>(constructFraction(minMaxFrac, minMaxFrac));
|
||||
? Precision::increment(increment)
|
||||
: static_cast<Precision>(Precision::fixedFraction(minMaxFrac));
|
||||
retval.fTrailingZeroDisplay = fTrailingZeroDisplay;
|
||||
return retval;
|
||||
}
|
||||
|
@ -288,7 +269,9 @@ Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const {
|
|||
Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
|
||||
if (fType == RND_ERROR) { return *this; } // no-op in error state
|
||||
if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
|
||||
return constructIncrement(fUnion.increment.fIncrement, minFrac);
|
||||
IncrementPrecision copy = *this;
|
||||
copy.fUnion.increment.fMinFrac = minFrac;
|
||||
return copy;
|
||||
} else {
|
||||
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
|
||||
}
|
||||
|
@ -333,25 +316,22 @@ Precision::constructFractionSignificant(
|
|||
return {RND_FRACTION_SIGNIFICANT, union_};
|
||||
}
|
||||
|
||||
IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
|
||||
IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) {
|
||||
IncrementSettings settings;
|
||||
// Note: For number formatting, fIncrement is used for RND_INCREMENT but not
|
||||
// RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all
|
||||
// three when constructing a skeleton.
|
||||
settings.fIncrement = increment;
|
||||
settings.fMinFrac = static_cast<digits_t>(minFrac);
|
||||
// One of the few pre-computed quantities:
|
||||
// Note: it is possible for minFrac to be more than maxFrac... (misleading)
|
||||
int8_t singleDigit;
|
||||
settings.fMaxFrac = roundingutils::doubleFractionLength(increment, &singleDigit);
|
||||
settings.fIncrementMagnitude = magnitude;
|
||||
settings.fMinFrac = magnitude > 0 ? 0 : -magnitude;
|
||||
PrecisionUnion union_;
|
||||
union_.increment = settings;
|
||||
if (singleDigit == 1) {
|
||||
if (increment == 1) {
|
||||
// NOTE: In C++, we must return the correct value type with the correct union.
|
||||
// It would be invalid to return a RND_FRACTION here because the methods on the
|
||||
// IncrementPrecision type assume that the union is backed by increment data.
|
||||
return {RND_INCREMENT_ONE, union_};
|
||||
} else if (singleDigit == 5) {
|
||||
} else if (increment == 5) {
|
||||
return {RND_INCREMENT_FIVE, union_};
|
||||
} else {
|
||||
return {RND_INCREMENT, union_};
|
||||
|
@ -524,6 +504,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
|
|||
case Precision::RND_INCREMENT:
|
||||
value.roundToIncrement(
|
||||
fPrecision.fUnion.increment.fIncrement,
|
||||
fPrecision.fUnion.increment.fIncrementMagnitude,
|
||||
fRoundingMode,
|
||||
status);
|
||||
resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
|
||||
|
@ -531,7 +512,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
|
|||
|
||||
case Precision::RND_INCREMENT_ONE:
|
||||
value.roundToMagnitude(
|
||||
-fPrecision.fUnion.increment.fMaxFrac,
|
||||
fPrecision.fUnion.increment.fIncrementMagnitude,
|
||||
fRoundingMode,
|
||||
status);
|
||||
resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
|
||||
|
@ -539,7 +520,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
|
|||
|
||||
case Precision::RND_INCREMENT_FIVE:
|
||||
value.roundToNickel(
|
||||
-fPrecision.fUnion.increment.fMaxFrac,
|
||||
fPrecision.fUnion.increment.fIncrementMagnitude,
|
||||
fRoundingMode,
|
||||
status);
|
||||
resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
|
||||
|
|
|
@ -174,15 +174,6 @@ inline bool roundsAtMidpoint(int roundingMode) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of fraction digits in a double. Used for computing maxFrac for an increment.
|
||||
* Calls into the DoubleToStringConverter library to do so.
|
||||
*
|
||||
* @param singleDigit An output parameter; set to a number if that is the
|
||||
* only digit in the double, or -1 if there is more than one digit.
|
||||
*/
|
||||
digits_t doubleFractionLength(double input, int8_t* singleDigit);
|
||||
|
||||
} // namespace roundingutils
|
||||
|
||||
|
||||
|
|
|
@ -1397,12 +1397,16 @@ void blueprint_helpers::parseIncrementOption(const StringSegment &segment, Macro
|
|||
number::impl::parseIncrementOption(segment, macros.precision, status);
|
||||
}
|
||||
|
||||
void blueprint_helpers::generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb,
|
||||
UErrorCode&) {
|
||||
void blueprint_helpers::generateIncrementOption(
|
||||
uint32_t increment,
|
||||
digits_t incrementMagnitude,
|
||||
int32_t minFrac,
|
||||
UnicodeString& sb,
|
||||
UErrorCode&) {
|
||||
// Utilize DecimalQuantity/double_conversion to format this for us.
|
||||
DecimalQuantity dq;
|
||||
dq.setToDouble(increment);
|
||||
dq.roundToInfinity();
|
||||
dq.setToLong(increment);
|
||||
dq.adjustMagnitude(incrementMagnitude);
|
||||
dq.setMinFraction(minFrac);
|
||||
sb.append(dq.toPlainString());
|
||||
}
|
||||
|
@ -1638,6 +1642,7 @@ bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UE
|
|||
sb.append(u"precision-increment/", -1);
|
||||
blueprint_helpers::generateIncrementOption(
|
||||
impl.fIncrement,
|
||||
impl.fIncrementMagnitude,
|
||||
impl.fMinFrac,
|
||||
sb,
|
||||
status);
|
||||
|
|
|
@ -286,7 +286,7 @@ bool parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, U
|
|||
void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
void
|
||||
generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb, UErrorCode& status);
|
||||
generateIncrementOption(uint32_t increment, digits_t incrementMagnitude, int32_t minFrac, UnicodeString& sb, UErrorCode& status);
|
||||
|
||||
void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
|
||||
|
||||
|
|
|
@ -640,6 +640,33 @@ class U_I18N_API Precision : public UMemory {
|
|||
*/
|
||||
static IncrementPrecision increment(double roundingIncrement);
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Version of `Precision::increment()` that takes an integer at a particular power of 10.
|
||||
*
|
||||
* To round to the nearest 0.5 and display 2 fraction digits, with this function, you should write one of the following:
|
||||
*
|
||||
* <pre>
|
||||
* Precision::incrementExact(5, -1).withMinFraction(2)
|
||||
* Precision::incrementExact(50, -2).withMinFraction(2)
|
||||
* Precision::incrementExact(50, -2)
|
||||
* </pre>
|
||||
*
|
||||
* This is analagous to ICU4J `Precision.increment(new BigDecimal("0.50"))`.
|
||||
*
|
||||
* This behavior is modeled after ECMA-402. For more information, see:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#roundingincrement
|
||||
*
|
||||
* @param mantissa
|
||||
* The increment to which to round numbers.
|
||||
* @param magnitude
|
||||
* The power of 10 of the ones digit of the mantissa.
|
||||
* @return A precision for chaining or passing to the NumberFormatter precision() setter.
|
||||
* @draft ICU 71
|
||||
*/
|
||||
static IncrementPrecision incrementExact(uint64_t mantissa, int16_t magnitude);
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
/**
|
||||
* Show numbers rounded and padded according to the rules for the currency unit. The most common
|
||||
* rounding precision settings for currencies include <code>Precision::fixedFraction(2)</code>,
|
||||
|
@ -714,12 +741,14 @@ class U_I18N_API Precision : public UMemory {
|
|||
/** @internal (private) */
|
||||
struct IncrementSettings {
|
||||
// For RND_INCREMENT, RND_INCREMENT_ONE, and RND_INCREMENT_FIVE
|
||||
// Note: This is a union, so we shouldn't own memory, since
|
||||
// the default destructor would leak it.
|
||||
/** @internal (private) */
|
||||
double fIncrement;
|
||||
uint64_t fIncrement;
|
||||
/** @internal (private) */
|
||||
impl::digits_t fIncrementMagnitude;
|
||||
/** @internal (private) */
|
||||
impl::digits_t fMinFrac;
|
||||
/** @internal (private) */
|
||||
impl::digits_t fMaxFrac;
|
||||
} increment;
|
||||
UCurrencyUsage currencyUsage; // For RND_CURRENCY
|
||||
UErrorCode errorCode; // For RND_ERROR
|
||||
|
@ -765,7 +794,7 @@ class U_I18N_API Precision : public UMemory {
|
|||
UNumberRoundingPriority priority,
|
||||
bool retain);
|
||||
|
||||
static IncrementPrecision constructIncrement(double increment, int32_t minFrac);
|
||||
static IncrementPrecision constructIncrement(uint64_t increment, impl::digits_t magnitude);
|
||||
|
||||
static CurrencyPrecision constructCurrency(UCurrencyUsage usage);
|
||||
|
||||
|
|
|
@ -3465,6 +3465,42 @@ void NumberFormatterApiTest::roundingOther() {
|
|||
u"0.0",
|
||||
u"0.0");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Large integer increment",
|
||||
u"precision-increment/24000000000000000000000",
|
||||
u"precision-increment/24000000000000000000000",
|
||||
NumberFormatter::with().precision(Precision::incrementExact(24, 21)),
|
||||
Locale::getEnglish(),
|
||||
3.1e22,
|
||||
u"24,000,000,000,000,000,000,000");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Quarter rounding",
|
||||
u"precision-increment/250",
|
||||
u"precision-increment/250",
|
||||
NumberFormatter::with().precision(Precision::incrementExact(250, 0)),
|
||||
Locale::getEnglish(),
|
||||
700,
|
||||
u"750");
|
||||
|
||||
assertFormatSingle(
|
||||
u"ECMA-402 limit",
|
||||
u"precision-increment/.00000000000000000020",
|
||||
u"precision-increment/.00000000000000000020",
|
||||
NumberFormatter::with().precision(Precision::incrementExact(20, -20)),
|
||||
Locale::getEnglish(),
|
||||
333e-20,
|
||||
u"0.00000000000000000340");
|
||||
|
||||
assertFormatSingle(
|
||||
u"ECMA-402 limit with increment = 1",
|
||||
u"precision-increment/.00000000000000000001",
|
||||
u"precision-increment/.00000000000000000001",
|
||||
NumberFormatter::with().precision(Precision::incrementExact(1, -20)),
|
||||
Locale::getEnglish(),
|
||||
4321e-21,
|
||||
u"0.00000000000000000432");
|
||||
|
||||
assertFormatDescending(
|
||||
u"Currency Standard",
|
||||
u"currency/CZK precision-currency-standard",
|
||||
|
@ -6011,7 +6047,7 @@ NumberFormatterApiTest::assertFormatSingle(
|
|||
// Only compare normalized skeletons: the tests need not provide the normalized forms.
|
||||
// Use the normalized form to construct the testing formatter to ensure no loss of info.
|
||||
UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
|
||||
assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
|
||||
assertEquals(message + ": Skeleton", normalized, f.toSkeleton(status));
|
||||
LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
|
||||
UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
|
||||
assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
|
||||
|
|
|
@ -101,8 +101,11 @@ void DecimalQuantityTest::testDecimalQuantityBehaviorStandalone() {
|
|||
assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
|
||||
fq.roundToInfinity();
|
||||
assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
|
||||
fq.roundToIncrement(0.005, RoundingMode::UNUM_ROUND_HALFEVEN, status);
|
||||
fq.roundToIncrement(4, -3, RoundingMode::UNUM_ROUND_HALFEVEN, status);
|
||||
assertSuccess("Rounding to increment", status);
|
||||
assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987656E-3>");
|
||||
fq.roundToNickel(-3, RoundingMode::UNUM_ROUND_HALFEVEN, status);
|
||||
assertSuccess("Rounding to nickel", status);
|
||||
assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987655E-3>");
|
||||
fq.roundToMagnitude(-2, RoundingMode::UNUM_ROUND_HALFEVEN, status);
|
||||
assertSuccess("Rounding to magnitude", status);
|
||||
|
|
|
@ -3357,6 +3357,7 @@ void NumberFormatTest::TestRoundingPattern() {
|
|||
|
||||
void NumberFormatTest::checkRounding(DecimalFormat* df, double base, int iterations, double increment) {
|
||||
df->setRoundingIncrement(increment);
|
||||
assertEquals("Rounding increment round-trip", increment, df->getRoundingIncrement());
|
||||
double lastParsed=INT32_MIN; //Intger.MIN_VALUE
|
||||
for (int i=-iterations; i<=iterations;i++) {
|
||||
double iValue=base+(increment*(i*0.1));
|
||||
|
|
|
@ -789,7 +789,7 @@ public abstract class Precision {
|
|||
@Override
|
||||
public void apply(DecimalQuantity value) {
|
||||
value.roundToIncrement(increment, mathContext);
|
||||
setResolvedMinFraction(value, increment.scale());
|
||||
setResolvedMinFraction(value, Math.max(0, increment.scale()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3461,6 +3461,42 @@ public class NumberFormatterApiTest extends TestFmwk {
|
|||
"0.0",
|
||||
"0.0");
|
||||
|
||||
assertFormatSingle(
|
||||
"Large integer increment",
|
||||
"precision-increment/24000000000000000000000",
|
||||
"precision-increment/24000000000000000000000",
|
||||
NumberFormatter.with().precision(Precision.increment(new BigDecimal("24e21"))),
|
||||
ULocale.ENGLISH,
|
||||
3.1e22,
|
||||
"24,000,000,000,000,000,000,000");
|
||||
|
||||
assertFormatSingle(
|
||||
"Quarter rounding",
|
||||
"precision-increment/250",
|
||||
"precision-increment/250",
|
||||
NumberFormatter.with().precision(Precision.increment(new BigDecimal("250"))),
|
||||
ULocale.ENGLISH,
|
||||
700,
|
||||
"750");
|
||||
|
||||
assertFormatSingle(
|
||||
"ECMA-402 limit",
|
||||
"precision-increment/.00000000000000000020",
|
||||
"precision-increment/.00000000000000000020",
|
||||
NumberFormatter.with().precision(Precision.increment(new BigDecimal("20e-20"))),
|
||||
ULocale.ENGLISH,
|
||||
333e-20,
|
||||
"0.00000000000000000340");
|
||||
|
||||
assertFormatSingle(
|
||||
"ECMA-402 limit with increment = 1",
|
||||
"precision-increment/.00000000000000000001",
|
||||
"precision-increment/.00000000000000000001",
|
||||
NumberFormatter.with().precision(Precision.increment(new BigDecimal("1e-20"))),
|
||||
ULocale.ENGLISH,
|
||||
4321e-21,
|
||||
"0.00000000000000000432");
|
||||
|
||||
assertFormatDescending(
|
||||
"Currency Standard",
|
||||
"currency/CZK precision-currency-standard",
|
||||
|
|
Loading…
Add table
Reference in a new issue