]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/number_rounding.cpp
ICU-64252.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / number_rounding.cpp
index ae4b8849fbe95638e81fe72e29147b418f6cc317..28ff1f13c9ce81a90a3ed3b5ceadcdc040b61a5d 100644 (file)
@@ -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<digits_t>(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<digits_t>(minSig);
+    settings.fMaxSig = static_cast<digits_t>(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<digits_t>(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) {
@@ -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);
+                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,15 +414,51 @@ 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.isZero() && fPrecision.fUnion.incrSig.fMinSig > 0) {
+                value.setMinInteger(1);
+            }
             break;
+
+        default:
+            UPRV_UNREACHABLE;
     }
 }
 
@@ -408,7 +466,7 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCod
     // 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);
+    value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
 }
 
 #endif /* #if !UCONFIG_NO_FORMATTING */