X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/0f5d89e82340278ed3d7d50029f37cab2c41a57e..HEAD:/icuSources/i18n/number_rounding.cpp?ds=sidebyside diff --git a/icuSources/i18n/number_rounding.cpp b/icuSources/i18n/number_rounding.cpp index ae4b8849..a89b8e5c 100644 --- a/icuSources/i18n/number_rounding.cpp +++ b/icuSources/i18n/number_rounding.cpp @@ -33,7 +33,7 @@ int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig if (maxSig == -1) { return INT32_MIN; } - int magnitude = value.isZero() ? 0 : value.getMagnitude(); + int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); return magnitude - maxSig + 1; } @@ -45,7 +45,7 @@ int32_t getDisplayMagnitudeFraction(int minFrac) { } int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) { - int magnitude = value.isZero() ? 0 : value.getMagnitude(); + int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); return magnitude - minSig + 1; } @@ -55,7 +55,7 @@ int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) MultiplierProducer::~MultiplierProducer() = default; -digits_t roundingutils::doubleFractionLength(double input) { +digits_t roundingutils::doubleFractionLength(double input, int8_t* singleDigit) { char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; bool sign; // unused; always positive int32_t length; @@ -71,6 +71,14 @@ digits_t roundingutils::doubleFractionLength(double input) { &point ); + if (singleDigit == nullptr) { + // no-op + } else if (length == 1) { + *singleDigit = buffer[0] - '0'; + } else { + *singleDigit = -1; + } + return static_cast(length - point); } @@ -161,13 +169,6 @@ CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) { return constructCurrency(currencyUsage); } -Precision Precision::withMode(RoundingMode roundingMode) const { - if (fType == RND_ERROR) { return *this; } // no-op in error state - Precision retval = *this; - retval.fRoundingMode = roundingMode; - return retval; -} - Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const { if (fType == RND_ERROR) { return *this; } // no-op in error state if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { @@ -242,6 +243,16 @@ Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) { return {RND_SIGNIFICANT, union_, kDefaultMode}; } +Precision Precision::constructIncrementSignificant(double increment, int32_t minSig, int32_t maxSig) { // Apple rdar://52538227 + IncrementSignificantSettings settings; + settings.fIncrement = increment; + settings.fMinSig = static_cast(minSig); + settings.fMaxSig = static_cast(maxSig); + PrecisionUnion union_; + union_.incrSig = settings; + return {RND_INCREMENT_SIGNIFICANT, union_, kDefaultMode}; +} + Precision Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) { FractionSignificantSettings settings = base.fUnion.fracSig; @@ -254,14 +265,27 @@ Precision::constructFractionSignificant(const FractionPrecision &base, int32_t m IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) { 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(minFrac); // One of the few pre-computed quantities: // Note: it is possible for minFrac to be more than maxFrac... (misleading) - settings.fMaxFrac = roundingutils::doubleFractionLength(increment); + int8_t singleDigit; + settings.fMaxFrac = roundingutils::doubleFractionLength(increment, &singleDigit); PrecisionUnion union_; union_.increment = settings; - return {RND_INCREMENT, union_, kDefaultMode}; + if (singleDigit == 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_, kDefaultMode}; + } else if (singleDigit == 5) { + return {RND_INCREMENT_FIVE, union_, kDefaultMode}; + } else { + return {RND_INCREMENT, union_, kDefaultMode}; + } } CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { @@ -292,8 +316,8 @@ bool RoundingImpl::isSignificantDigits() const { int32_t RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, UErrorCode &status) { - // Do not call this method with zero. - U_ASSERT(!input.isZero()); + // Do not call this method with zero, NaN, or infinity. + U_ASSERT(!input.isZeroish()); // Perform the first attempt at rounding. int magnitude = input.getMagnitude(); @@ -302,7 +326,7 @@ RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl: apply(input, status); // If the number rounded to zero, exit. - if (input.isZero() || U_FAILURE(status)) { + if (input.isZeroish() || U_FAILURE(status)) { return multiplier; } @@ -348,9 +372,8 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac), fRoundingMode, status); - value.setFractionLength( - uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)), - INT32_MAX); + value.setMinFraction( + uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac))); break; case Precision::RND_SIGNIFICANT: @@ -358,12 +381,11 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig), fRoundingMode, status); - value.setFractionLength( - uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)), - INT32_MAX); + value.setMinFraction( + uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig))); // Make sure that digits are displayed on zero. - if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) { - value.setIntegerLength(1, INT32_MAX); + if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) { + value.setMinInteger(1); } break; @@ -384,7 +406,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const roundingMag = uprv_min(roundingMag, candidate); } value.roundToMagnitude(roundingMag, fRoundingMode, status); - value.setFractionLength(uprv_max(0, -displayMag), INT32_MAX); + value.setMinFraction(uprv_max(0, -displayMag)); break; } @@ -392,23 +414,59 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const value.roundToIncrement( fPrecision.fUnion.increment.fIncrement, fRoundingMode, - fPrecision.fUnion.increment.fMaxFrac, status); - value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX); + value.setMinFraction(fPrecision.fUnion.increment.fMinFrac); + break; + + case Precision::RND_INCREMENT_ONE: + value.roundToMagnitude( + -fPrecision.fUnion.increment.fMaxFrac, + fRoundingMode, + status); + value.setMinFraction(fPrecision.fUnion.increment.fMinFrac); + break; + + case Precision::RND_INCREMENT_FIVE: + value.roundToNickel( + -fPrecision.fUnion.increment.fMaxFrac, + fRoundingMode, + status); + value.setMinFraction(fPrecision.fUnion.increment.fMinFrac); break; case Precision::RND_CURRENCY: // Call .withCurrency() before .apply()! - U_ASSERT(false); + UPRV_UNREACHABLE; + + case Precision::RND_INCREMENT_SIGNIFICANT: // Apple addition rdar://52538227 + // FIrst round to increment + value.roundToIncrement( + fPrecision.fUnion.incrSig.fIncrement, + fRoundingMode, + status); + // Then round to significant digits + value.roundToMagnitude( + getRoundingMagnitudeSignificant(value, fPrecision.fUnion.incrSig.fMaxSig), + fRoundingMode, + status); + value.setMinFraction( + uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.incrSig.fMinSig))); + // Make sure that digits are displayed on zero. + if (value.isZeroish() && fPrecision.fUnion.incrSig.fMinSig > 0) { + value.setMinInteger(1); + } break; + + default: + UPRV_UNREACHABLE; } } void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) { // This method is intended for the one specific purpose of helping print "00.000E0". U_ASSERT(isSignificantDigits()); - U_ASSERT(value.isZero()); - value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX); + U_ASSERT(value.isZeroish()); + value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt); } #endif /* #if !UCONFIG_NO_FORMATTING */