summaryrefslogtreecommitdiff
path: root/Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp')
-rw-r--r--Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp155
1 files changed, 91 insertions, 64 deletions
diff --git a/Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp b/Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp
index 877df63c8f6..a9b3f16c050 100644
--- a/Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp
+++ b/Build/source/libs/icu/icu-src/source/i18n/number_rounding.cpp
@@ -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};
}
@@ -226,7 +207,8 @@ Precision FractionPrecision::withSignificantDigits(
*this,
minSignificantDigits,
maxSignificantDigits,
- priority);
+ priority,
+ false);
} else {
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
}
@@ -239,7 +221,8 @@ Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
*this,
1,
minSignificantDigits,
- UNUM_ROUNDING_PRIORITY_RELAXED);
+ UNUM_ROUNDING_PRIORITY_RELAXED,
+ true);
} else {
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
}
@@ -251,7 +234,8 @@ Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
return constructFractionSignificant(*this,
1,
maxSignificantDigits,
- UNUM_ROUNDING_PRIORITY_STRICT);
+ UNUM_ROUNDING_PRIORITY_STRICT,
+ true);
} else {
return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
}
@@ -266,8 +250,8 @@ Precision Precision::withCurrency(const CurrencyUnit &currency, 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;
}
@@ -285,7 +269,9 @@ Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) 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};
}
@@ -318,35 +304,34 @@ Precision::constructFractionSignificant(
const FractionPrecision &base,
int32_t minSig,
int32_t maxSig,
- UNumberRoundingPriority priority) {
+ UNumberRoundingPriority priority,
+ bool retain) {
FractionSignificantSettings settings = base.fUnion.fracSig;
settings.fMinSig = static_cast<digits_t>(minSig);
settings.fMaxSig = static_cast<digits_t>(maxSig);
settings.fPriority = priority;
+ settings.fRetain = retain;
PrecisionUnion union_;
union_.fracSig = settings;
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_};
@@ -457,6 +442,23 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
break;
case Precision::RND_FRACTION_SIGNIFICANT: {
+ // From ECMA-402:
+ /*
+ Let sResult be ToRawPrecision(...).
+ Let fResult be ToRawFixed(...).
+ If intlObj.[[RoundingType]] is morePrecision, then
+ If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
+ Let result be sResult.
+ Else,
+ Let result be fResult.
+ Else,
+ Assert: intlObj.[[RoundingType]] is lessPrecision.
+ If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
+ Let result be fResult.
+ Else,
+ Let result be sResult.
+ */
+
int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig);
int32_t roundingMag;
@@ -465,11 +467,35 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
} else {
roundingMag = uprv_max(roundingMag1, roundingMag2);
}
- value.roundToMagnitude(roundingMag, fRoundingMode, status);
+ if (!value.isZeroish()) {
+ int32_t upperMag = value.getMagnitude();
+ value.roundToMagnitude(roundingMag, fRoundingMode, status);
+ if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) {
+ // roundingMag2 needs to be the magnitude after rounding
+ roundingMag2 += 1;
+ }
+ }
int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig);
- int32_t displayMag = uprv_min(displayMag1, displayMag2);
+ int32_t displayMag;
+ if (fPrecision.fUnion.fracSig.fRetain) {
+ // withMinDigits + withMaxDigits
+ displayMag = uprv_min(displayMag1, displayMag2);
+ } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
+ if (roundingMag2 <= roundingMag1) {
+ displayMag = displayMag2;
+ } else {
+ displayMag = displayMag1;
+ }
+ } else {
+ U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT);
+ if (roundingMag2 <= roundingMag1) {
+ displayMag = displayMag1;
+ } else {
+ displayMag = displayMag2;
+ }
+ }
resolvedMinFraction = uprv_max(0, -displayMag);
break;
@@ -478,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;
@@ -485,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;
@@ -493,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;