diff --git a/icu4c/source/i18n/number_compact.cpp b/icu4c/source/i18n/number_compact.cpp
index c19d495fd11..7cadba688a1 100644
--- a/icu4c/source/i18n/number_compact.cpp
+++ b/icu4c/source/i18n/number_compact.cpp
@@ -275,10 +275,10 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr
     int magnitude;
     if (quantity.isZero()) {
         magnitude = 0;
-        micros.rounding.apply(quantity, status);
+        micros.rounder.apply(quantity, status);
     } else {
         // TODO: Revisit chooseMultiplierAndApply
-        int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data, status);
+        int multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status);
         magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
         magnitude -= multiplier;
     }
@@ -313,7 +313,7 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr
     }
 
     // We already performed rounding. Do not perform it again.
-    micros.rounding = Rounder::constructPassThrough();
+    micros.rounder = RoundingImpl::passThrough();
 }
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp
index 18f11c4a328..738b075a064 100644
--- a/icu4c/source/i18n/number_decimalquantity.cpp
+++ b/icu4c/source/i18n/number_decimalquantity.cpp
@@ -132,7 +132,7 @@ void DecimalQuantity::clear() {
 }
 
 void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) {
-    // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
+    // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
     U_ASSERT(minInt >= 0);
     U_ASSERT(maxInt >= minInt);
 
@@ -149,7 +149,7 @@ void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) {
 }
 
 void DecimalQuantity::setFractionLength(int32_t minFrac, int32_t maxFrac) {
-    // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
+    // Validation should happen outside of DecimalQuantity, e.g., in the Precision class.
     U_ASSERT(minFrac >= 0);
     U_ASSERT(maxFrac >= minFrac);
 
diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp
index 459cb04bb8d..ea4c10156a4 100644
--- a/icu4c/source/i18n/number_fluent.cpp
+++ b/icu4c/source/i18n/number_fluent.cpp
@@ -117,18 +117,32 @@ Derived NumberFormatterSettings<Derived>::adoptPerUnit(icu::MeasureUnit* perUnit
 }
 
 template<typename Derived>
-Derived NumberFormatterSettings<Derived>::rounding(const Rounder& rounder) const& {
+Derived NumberFormatterSettings<Derived>::precision(const Precision& precision) const& {
     Derived copy(*this);
     // NOTE: Slicing is OK.
-    copy.fMacros.rounder = rounder;
+    copy.fMacros.precision = precision;
     return copy;
 }
 
 template<typename Derived>
-Derived NumberFormatterSettings<Derived>::rounding(const Rounder& rounder)&& {
+Derived NumberFormatterSettings<Derived>::precision(const Precision& precision)&& {
     Derived move(std::move(*this));
     // NOTE: Slicing is OK.
-    move.fMacros.rounder = rounder;
+    move.fMacros.precision = precision;
+    return move;
+}
+
+template<typename Derived>
+Derived NumberFormatterSettings<Derived>::roundingMode(UNumberFormatRoundingMode roundingMode) const& {
+    Derived copy(*this);
+    copy.fMacros.roundingMode = roundingMode;
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberFormatterSettings<Derived>::roundingMode(UNumberFormatRoundingMode roundingMode)&& {
+    Derived move(std::move(*this));
+    move.fMacros.roundingMode = roundingMode;
     return move;
 }
 
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index a6bda29f3e3..dc493e1dddb 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -246,16 +246,24 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
     }
 
     // Rounding strategy
-    if (!macros.rounder.isBogus()) {
-        fMicros.rounding = macros.rounder;
+    Precision precision;
+    if (!macros.precision.isBogus()) {
+        precision = macros.precision;
     } else if (macros.notation.fType == Notation::NTN_COMPACT) {
-        fMicros.rounding = Rounder::integer().withMinDigits(2);
+        precision = Precision::integer().withMinDigits(2);
     } else if (isCurrency) {
-        fMicros.rounding = Rounder::currency(UCURR_USAGE_STANDARD);
+        precision = Precision::currency(UCURR_USAGE_STANDARD);
     } else {
-        fMicros.rounding = Rounder::maxFraction(6);
+        precision = Precision::maxFraction(6);
     }
-    fMicros.rounding.setLocaleData(currency, status);
+    UNumberFormatRoundingMode roundingMode;
+    if (macros.roundingMode != kDefaultMode) {
+        roundingMode = macros.roundingMode;
+    } else {
+        // Temporary until ICU 64
+        roundingMode = precision.fRoundingMode;
+    }
+    fMicros.rounder = {precision, roundingMode, currency, status};
 
     // Grouping strategy
     if (!macros.grouper.isBogus()) {
@@ -397,7 +405,7 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Local
 
 int32_t NumberFormatterImpl::microsToString(const MicroProps& micros, DecimalQuantity& quantity,
                                             NumberStringBuilder& string, UErrorCode& status) {
-    micros.rounding.apply(quantity, status);
+    micros.rounder.apply(quantity, status);
     micros.integerWidth.apply(quantity, status);
     int32_t length = writeNumber(micros, quantity, string, status);
     // NOTE: When range formatting is added, these modifiers can bubble up.
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp
index e84766cbf29..cc0dd78d6d2 100644
--- a/icu4c/source/i18n/number_longnames.cpp
+++ b/icu4c/source/i18n/number_longnames.cpp
@@ -260,7 +260,7 @@ void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &mic
     parent->processQuantity(quantity, micros, status);
     // TODO: Avoid the copy here?
     DecimalQuantity copy(quantity);
-    micros.rounding.apply(copy, status);
+    micros.rounder.apply(copy, status);
     micros.modOuter = &fModifiers[copy.getStandardPlural(rules)];
 }
 
diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp
index 51dd0a9c8c8..9df0e2d73ac 100644
--- a/icu4c/source/i18n/number_mapper.cpp
+++ b/icu4c/source/i18n/number_mapper.cpp
@@ -106,7 +106,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
     // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
     // maxFrac was
     // set (but not both) on a currency instance.
-    // NOTE: Increments are handled in "Rounder.constructCurrency()".
+    // NOTE: Increments are handled in "Precision.constructCurrency()".
     if (useCurrency && (minFrac == -1 || maxFrac == -1)) {
         int32_t digits = ucurr_getDefaultFractionDigitsForUsage(
                 currency.getISOCurrency(), currencyUsage, &status);
@@ -135,24 +135,24 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
         minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt;
         maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt;
     }
-    Rounder rounding;
+    Precision precision;
     if (!properties.currencyUsage.isNull()) {
-        rounding = Rounder::constructCurrency(currencyUsage).withCurrency(currency);
+        precision = Precision::constructCurrency(currencyUsage).withCurrency(currency);
     } else if (roundingIncrement != 0.0) {
-        rounding = Rounder::constructIncrement(roundingIncrement, minFrac);
+        precision = Precision::constructIncrement(roundingIncrement, minFrac);
     } else if (explicitMinMaxSig) {
         minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig;
         maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig
                                                                           ? kMaxIntFracSig : maxSig;
-        rounding = Rounder::constructSignificant(minSig, maxSig);
+        precision = Precision::constructSignificant(minSig, maxSig);
     } else if (explicitMinMaxFrac) {
-        rounding = Rounder::constructFraction(minFrac, maxFrac);
+        precision = Precision::constructFraction(minFrac, maxFrac);
     } else if (useCurrency) {
-        rounding = Rounder::constructCurrency(currencyUsage);
+        precision = Precision::constructCurrency(currencyUsage);
     }
-    if (!rounding.isBogus()) {
-        rounding = rounding.withMode(roundingMode);
-        macros.rounder = rounding;
+    if (!precision.isBogus()) {
+        precision = precision.withMode(roundingMode);
+        macros.precision = precision;
     }
 
     ///////////////////
@@ -221,7 +221,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
                 properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO);
         // Scientific notation also involves overriding the rounding mode.
         // TODO: Overriding here is a bit of a hack. Should this logic go earlier?
-        if (macros.rounder.fType == Rounder::RounderType::RND_FRACTION) {
+        if (macros.precision.fType == Precision::PrecisionType::RND_FRACTION) {
             // For the purposes of rounding, get the original min/max int/frac, since the local
             // variables
             // have been manipulated for display purposes.
@@ -230,13 +230,13 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
             int maxFrac_ = properties.maximumFractionDigits;
             if (minInt_ == 0 && maxFrac_ == 0) {
                 // Patterns like "#E0" and "##E0", which mean no rounding!
-                macros.rounder = Rounder::unlimited().withMode(roundingMode);
+                macros.precision = Precision::unlimited().withMode(roundingMode);
             } else if (minInt_ == 0 && minFrac_ == 0) {
                 // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
-                macros.rounder = Rounder::constructSignificant(1, maxFrac_ + 1).withMode(roundingMode);
+                macros.precision = Precision::constructSignificant(1, maxFrac_ + 1).withMode(roundingMode);
             } else {
                 // All other scientific patterns, which mean round to minInt+maxFrac
-                macros.rounder = Rounder::constructSignificant(
+                macros.precision = Precision::constructSignificant(
                         minInt_ + minFrac_, minInt_ + maxFrac_).withMode(roundingMode);
             }
         }
@@ -273,25 +273,25 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
         exportedProperties->minimumIntegerDigits = minInt;
         exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt;
 
-        Rounder rounding_;
-        if (rounding.fType == Rounder::RounderType::RND_CURRENCY) {
-            rounding_ = rounding.withCurrency(currency, status);
+        Precision rounding_;
+        if (precision.fType == Precision::PrecisionType::RND_CURRENCY) {
+            rounding_ = precision.withCurrency(currency, status);
         } else {
-            rounding_ = rounding;
+            rounding_ = precision;
         }
         int minFrac_ = minFrac;
         int maxFrac_ = maxFrac;
         int minSig_ = minSig;
         int maxSig_ = maxSig;
         double increment_ = 0.0;
-        if (rounding_.fType == Rounder::RounderType::RND_FRACTION) {
+        if (rounding_.fType == Precision::PrecisionType::RND_FRACTION) {
             minFrac_ = rounding_.fUnion.fracSig.fMinFrac;
             maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac;
-        } else if (rounding_.fType == Rounder::RounderType::RND_INCREMENT) {
+        } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT) {
             increment_ = rounding_.fUnion.increment.fIncrement;
             minFrac_ = rounding_.fUnion.increment.fMinFrac;
             maxFrac_ = rounding_.fUnion.increment.fMinFrac;
-        } else if (rounding_.fType == Rounder::RounderType::RND_SIGNIFICANT) {
+        } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) {
             minSig_ = rounding_.fUnion.fracSig.fMinSig;
             maxSig_ = rounding_.fUnion.fracSig.fMaxSig;
         }
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp
index 45f790e571b..b45647d143d 100644
--- a/icu4c/source/i18n/number_patternmodifier.cpp
+++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -165,7 +165,7 @@ void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& mi
     if (needsPlurals()) {
         // TODO: Fix this. Avoid the copy.
         DecimalQuantity copy(fq);
-        micros.rounding.apply(copy, status);
+        micros.rounder.apply(copy, status);
         nonConstThis->setNumberProperties(fq.signum(), copy.getStandardPlural(rules));
     } else {
         nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
diff --git a/icu4c/source/i18n/number_rounding.cpp b/icu4c/source/i18n/number_rounding.cpp
index 137848ec469..ae4b8849fbe 100644
--- a/icu4c/source/i18n/number_rounding.cpp
+++ b/icu4c/source/i18n/number_rounding.cpp
@@ -75,15 +75,15 @@ digits_t roundingutils::doubleFractionLength(double input) {
 }
 
 
-Rounder Rounder::unlimited() {
-    return Rounder(RND_NONE, {}, kDefaultMode);
+Precision Precision::unlimited() {
+    return Precision(RND_NONE, {}, kDefaultMode);
 }
 
-FractionRounder Rounder::integer() {
+FractionPrecision Precision::integer() {
     return constructFraction(0, 0);
 }
 
-FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) {
+FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
     if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
         return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
     } else {
@@ -91,7 +91,7 @@ FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) {
     }
 }
 
-FractionRounder Rounder::minFraction(int32_t minFractionPlaces) {
+FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
     if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
         return constructFraction(minFractionPlaces, -1);
     } else {
@@ -99,7 +99,7 @@ FractionRounder Rounder::minFraction(int32_t minFractionPlaces) {
     }
 }
 
-FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) {
+FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
     if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
         return constructFraction(0, maxFractionPlaces);
     } else {
@@ -107,7 +107,7 @@ FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) {
     }
 }
 
-FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
+FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
     if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
         minFractionPlaces <= maxFractionPlaces) {
         return constructFraction(minFractionPlaces, maxFractionPlaces);
@@ -116,7 +116,7 @@ FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFr
     }
 }
 
-Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) {
+Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
     if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
         return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
     } else {
@@ -124,7 +124,7 @@ Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) {
     }
 }
 
-Rounder Rounder::minDigits(int32_t minSignificantDigits) {
+Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
         return constructSignificant(minSignificantDigits, -1);
     } else {
@@ -132,7 +132,7 @@ Rounder Rounder::minDigits(int32_t minSignificantDigits) {
     }
 }
 
-Rounder Rounder::maxDigits(int32_t maxSignificantDigits) {
+Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
         return constructSignificant(1, maxSignificantDigits);
     } else {
@@ -140,7 +140,7 @@ Rounder Rounder::maxDigits(int32_t maxSignificantDigits) {
     }
 }
 
-Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
+Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
     if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
         minSignificantDigits <= maxSignificantDigits) {
         return constructSignificant(minSignificantDigits, maxSignificantDigits);
@@ -149,7 +149,7 @@ Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignifica
     }
 }
 
-IncrementRounder Rounder::increment(double roundingIncrement) {
+IncrementPrecision Precision::increment(double roundingIncrement) {
     if (roundingIncrement > 0.0) {
         return constructIncrement(roundingIncrement, 0);
     } else {
@@ -157,16 +157,18 @@ IncrementRounder Rounder::increment(double roundingIncrement) {
     }
 }
 
-CurrencyRounder Rounder::currency(UCurrencyUsage currencyUsage) {
+CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
     return constructCurrency(currencyUsage);
 }
 
-Rounder Rounder::withMode(RoundingMode roundingMode) const {
+Precision Precision::withMode(RoundingMode roundingMode) const {
     if (fType == RND_ERROR) { return *this; } // no-op in error state
-    return {fType, fUnion, roundingMode};
+    Precision retval = *this;
+    retval.fRoundingMode = roundingMode;
+    return retval;
 }
 
-Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const {
+Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
     if (fType == RND_ERROR) { return *this; } // no-op in error state
     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
         return constructFractionSignificant(*this, minSignificantDigits, -1);
@@ -175,7 +177,7 @@ Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const {
     }
 }
 
-Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const {
+Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
     if (fType == RND_ERROR) { return *this; } // no-op in error state
     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
         return constructFractionSignificant(*this, -1, maxSignificantDigits);
@@ -185,7 +187,7 @@ Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const {
 }
 
 // Private method on base class
-Rounder Rounder::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
+Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
     if (fType == RND_ERROR) { return *this; } // no-op in error state
     U_ASSERT(fType == RND_CURRENCY);
     const char16_t *isoCode = currency.getISOCurrency();
@@ -199,17 +201,17 @@ Rounder Rounder::withCurrency(const CurrencyUnit &currency, UErrorCode &status)
     }
 }
 
-// Public method on CurrencyRounder subclass
-Rounder CurrencyRounder::withCurrency(const CurrencyUnit &currency) const {
+// Public method on CurrencyPrecision subclass
+Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
     UErrorCode localStatus = U_ZERO_ERROR;
-    Rounder result = Rounder::withCurrency(currency, localStatus);
+    Precision result = Precision::withCurrency(currency, localStatus);
     if (U_FAILURE(localStatus)) {
         return {localStatus};
     }
     return result;
 }
 
-Rounder IncrementRounder::withMinFraction(int32_t minFrac) 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);
@@ -218,70 +220,77 @@ Rounder IncrementRounder::withMinFraction(int32_t minFrac) const {
     }
 }
 
-FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) {
+FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
     FractionSignificantSettings settings;
     settings.fMinFrac = static_cast<digits_t>(minFrac);
     settings.fMaxFrac = static_cast<digits_t>(maxFrac);
     settings.fMinSig = -1;
     settings.fMaxSig = -1;
-    RounderUnion union_;
+    PrecisionUnion union_;
     union_.fracSig = settings;
     return {RND_FRACTION, union_, kDefaultMode};
 }
 
-Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) {
+Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
     FractionSignificantSettings settings;
     settings.fMinFrac = -1;
     settings.fMaxFrac = -1;
     settings.fMinSig = static_cast<digits_t>(minSig);
     settings.fMaxSig = static_cast<digits_t>(maxSig);
-    RounderUnion union_;
+    PrecisionUnion union_;
     union_.fracSig = settings;
     return {RND_SIGNIFICANT, union_, kDefaultMode};
 }
 
-Rounder
-Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) {
+Precision
+Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) {
     FractionSignificantSettings settings = base.fUnion.fracSig;
     settings.fMinSig = static_cast<digits_t>(minSig);
     settings.fMaxSig = static_cast<digits_t>(maxSig);
-    RounderUnion union_;
+    PrecisionUnion union_;
     union_.fracSig = settings;
     return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
 }
 
-IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) {
+IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
     IncrementSettings settings;
     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);
-    RounderUnion union_;
+    PrecisionUnion union_;
     union_.increment = settings;
     return {RND_INCREMENT, union_, kDefaultMode};
 }
 
-CurrencyRounder Rounder::constructCurrency(UCurrencyUsage usage) {
-    RounderUnion union_;
+CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
+    PrecisionUnion union_;
     union_.currencyUsage = usage;
     return {RND_CURRENCY, union_, kDefaultMode};
 }
 
-Rounder Rounder::constructPassThrough() {
-    RounderUnion union_;
-    union_.errorCode = U_ZERO_ERROR; // initialize the variable
-    return {RND_PASS_THROUGH, union_, kDefaultMode};
-}
 
-void Rounder::setLocaleData(const CurrencyUnit &currency, UErrorCode &status) {
-    if (fType == RND_CURRENCY) {
-        *this = withCurrency(currency, status);
+RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
+                           const CurrencyUnit& currency, UErrorCode& status)
+        : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
+    if (precision.fType == Precision::RND_CURRENCY) {
+        fPrecision = precision.withCurrency(currency, status);
     }
 }
 
+RoundingImpl RoundingImpl::passThrough() {
+    RoundingImpl retval;
+    retval.fPassThrough = true;
+    return retval;
+}
+
+bool RoundingImpl::isSignificantDigits() const {
+    return fPrecision.fType == Precision::RND_SIGNIFICANT;
+}
+
 int32_t
-Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
+RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
                                   UErrorCode &status) {
     // Do not call this method with zero.
     U_ASSERT(!input.isZero());
@@ -319,49 +328,59 @@ Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::Mult
 }
 
 /** This is the method that contains the actual rounding logic. */
-void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
-    switch (fType) {
-        case RND_BOGUS:
-        case RND_ERROR:
+void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
+    if (fPassThrough) {
+        return;
+    }
+    switch (fPrecision.fType) {
+        case Precision::RND_BOGUS:
+        case Precision::RND_ERROR:
             // Errors should be caught before the apply() method is called
             status = U_INTERNAL_PROGRAM_ERROR;
             break;
 
-        case RND_NONE:
+        case Precision::RND_NONE:
             value.roundToInfinity();
             break;
 
-        case RND_FRACTION:
+        case Precision::RND_FRACTION:
             value.roundToMagnitude(
-                    getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac), fRoundingMode, status);
-            value.setFractionLength(
-                    uprv_max(0, -getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac)), INT32_MAX);
-            break;
-
-        case RND_SIGNIFICANT:
-            value.roundToMagnitude(
-                    getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig),
+                    getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
                     fRoundingMode,
                     status);
             value.setFractionLength(
-                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)),
+                    uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)),
+                    INT32_MAX);
+            break;
+
+        case Precision::RND_SIGNIFICANT:
+            value.roundToMagnitude(
+                    getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
+                    fRoundingMode,
+                    status);
+            value.setFractionLength(
+                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)),
                     INT32_MAX);
             // Make sure that digits are displayed on zero.
-            if (value.isZero() && fUnion.fracSig.fMinSig > 0) {
+            if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) {
                 value.setIntegerLength(1, INT32_MAX);
             }
             break;
 
-        case RND_FRACTION_SIGNIFICANT: {
-            int32_t displayMag = getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac);
-            int32_t roundingMag = getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac);
-            if (fUnion.fracSig.fMinSig == -1) {
+        case Precision::RND_FRACTION_SIGNIFICANT: {
+            int32_t displayMag = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
+            int32_t roundingMag = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
+            if (fPrecision.fUnion.fracSig.fMinSig == -1) {
                 // Max Sig override
-                int32_t candidate = getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig);
+                int32_t candidate = getRoundingMagnitudeSignificant(
+                        value,
+                        fPrecision.fUnion.fracSig.fMaxSig);
                 roundingMag = uprv_max(roundingMag, candidate);
             } else {
                 // Min Sig override
-                int32_t candidate = getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig);
+                int32_t candidate = getDisplayMagnitudeSignificant(
+                        value,
+                        fPrecision.fUnion.fracSig.fMinSig);
                 roundingMag = uprv_min(roundingMag, candidate);
             }
             value.roundToMagnitude(roundingMag, fRoundingMode, status);
@@ -369,30 +388,27 @@ void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
             break;
         }
 
-        case RND_INCREMENT:
+        case Precision::RND_INCREMENT:
             value.roundToIncrement(
-                fUnion.increment.fIncrement,
-                fRoundingMode,
-                fUnion.increment.fMaxFrac,
-                status);
-            value.setFractionLength(fUnion.increment.fMinFrac, INT32_MAX);
+                    fPrecision.fUnion.increment.fIncrement,
+                    fRoundingMode,
+                    fPrecision.fUnion.increment.fMaxFrac,
+                    status);
+            value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX);
             break;
 
-        case RND_CURRENCY:
+        case Precision::RND_CURRENCY:
             // Call .withCurrency() before .apply()!
             U_ASSERT(false);
             break;
-
-        case RND_PASS_THROUGH:
-            break;
     }
 }
 
-void Rounder::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
+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(fType == RND_SIGNIFICANT);
+    U_ASSERT(isSignificantDigits());
     U_ASSERT(value.isZero());
-    value.setFractionLength(fUnion.fracSig.fMinSig - minInt, INT32_MAX);
+    value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX);
 }
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_roundingutils.h b/icu4c/source/i18n/number_roundingutils.h
index 0201623ac85..66d58bb775b 100644
--- a/icu4c/source/i18n/number_roundingutils.h
+++ b/icu4c/source/i18n/number_roundingutils.h
@@ -138,6 +138,55 @@ inline bool roundsAtMidpoint(int roundingMode) {
 digits_t doubleFractionLength(double input);
 
 } // namespace roundingutils
+
+
+/**
+ * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
+ *
+ * This class does not exist in Java: instead, the base Precision class is used.
+ */
+class RoundingImpl {
+  public:
+    RoundingImpl() = default;  // default constructor: leaves object in undefined state
+
+    RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
+                 const CurrencyUnit& currency, UErrorCode& status);
+
+    static RoundingImpl passThrough();
+
+    /** Required for ScientificFormatter */
+    bool isSignificantDigits() const;
+
+    /**
+     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
+     * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
+     *
+     * <p>
+     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
+     * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
+     * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
+     * change your multiplier to be -6, and you get 1.0E6, which is correct.
+     *
+     * @param input The quantity to process.
+     * @param producer Function to call to return a multiplier based on a magnitude.
+     * @return The number of orders of magnitude the input was adjusted by this method.
+     */
+    int32_t
+    chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
+                             UErrorCode &status);
+
+    void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
+
+    /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
+    void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
+
+  private:
+    Precision fPrecision;
+    UNumberFormatRoundingMode fRoundingMode;
+    bool fPassThrough;
+};
+
+
 } // namespace impl
 } // namespace number
 U_NAMESPACE_END
diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp
index b5490536e72..72288f208a3 100644
--- a/icu4c/source/i18n/number_scientific.cpp
+++ b/icu4c/source/i18n/number_scientific.cpp
@@ -106,16 +106,16 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
     // Treat zero as if it had magnitude 0
     int32_t exponent;
     if (quantity.isZero()) {
-        if (fSettings.fRequireMinInt && micros.rounding.fType == Rounder::RND_SIGNIFICANT) {
+        if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
             // Show "00.000E0" on pattern "00.000E0"
-            micros.rounding.apply(quantity, fSettings.fEngineeringInterval, status);
+            micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
             exponent = 0;
         } else {
-            micros.rounding.apply(quantity, status);
+            micros.rounder.apply(quantity, status);
             exponent = 0;
         }
     } else {
-        exponent = -micros.rounding.chooseMultiplierAndApply(quantity, *this, status);
+        exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);
     }
 
     // Use MicroProps's helper ScientificModifier and save it as the modInner.
@@ -124,7 +124,7 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
     micros.modInner = &mod;
 
     // We already performed rounding. Do not perform it again.
-    micros.rounding = Rounder::constructPassThrough();
+    micros.rounder = RoundingImpl::passThrough();
 }
 
 int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp
index ec7041d3374..bae38ac445e 100644
--- a/icu4c/source/i18n/number_skeletons.cpp
+++ b/icu4c/source/i18n/number_skeletons.cpp
@@ -53,10 +53,18 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
     b.add(u"base-unit", STEM_BASE_UNIT, status);
     b.add(u"percent", STEM_PERCENT, status);
     b.add(u"permille", STEM_PERMILLE, status);
-    b.add(u"round-integer", STEM_ROUND_INTEGER, status);
-    b.add(u"round-unlimited", STEM_ROUND_UNLIMITED, status);
-    b.add(u"round-currency-standard", STEM_ROUND_CURRENCY_STANDARD, status);
-    b.add(u"round-currency-cash", STEM_ROUND_CURRENCY_CASH, status);
+    b.add(u"precision-integer", STEM_PRECISION_INTEGER, status);
+    b.add(u"precision-unlimited", STEM_PRECISION_UNLIMITED, status);
+    b.add(u"precision-currency-standard", STEM_PRECISION_CURRENCY_STANDARD, status);
+    b.add(u"precision-currency-cash", STEM_PRECISION_CURRENCY_CASH, status);
+    b.add(u"rounding-mode-ceiling", STEM_ROUNDING_MODE_CEILING, status);
+    b.add(u"rounding-mode-floor", STEM_ROUNDING_MODE_FLOOR, status);
+    b.add(u"rounding-mode-down", STEM_ROUNDING_MODE_DOWN, status);
+    b.add(u"rounding-mode-up", STEM_ROUNDING_MODE_UP, status);
+    b.add(u"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN, status);
+    b.add(u"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN, status);
+    b.add(u"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP, status);
+    b.add(u"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY, status);
     b.add(u"group-off", STEM_GROUP_OFF, status);
     b.add(u"group-min2", STEM_GROUP_MIN2, status);
     b.add(u"group-auto", STEM_GROUP_AUTO, status);
@@ -80,7 +88,7 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
     if (U_FAILURE(status)) { return; }
 
     // Section 2:
-    b.add(u"round-increment", STEM_ROUND_INCREMENT, status);
+    b.add(u"precision-increment", STEM_PRECISION_INCREMENT, status);
     b.add(u"measure-unit", STEM_MEASURE_UNIT, status);
     b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status);
     b.add(u"currency", STEM_CURRENCY, status);
@@ -134,16 +142,6 @@ inline void appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) {
 }
 
 
-// NOTE: The order of these strings must be consistent with UNumberFormatRoundingMode
-const char16_t* const kRoundingModeStrings[] = {
-        u"ceiling", u"floor", u"down", u"up", u"half-even", u"half-down", u"half-up", u"unnecessary"};
-
-constexpr int32_t kRoundingModeCount = 8;
-static_assert(
-        sizeof(kRoundingModeStrings) / sizeof(*kRoundingModeStrings) == kRoundingModeCount,
-        "kRoundingModeCount should be the number of rounding modes");
-
-
 } // anonymous namespace
 
 
@@ -182,19 +180,43 @@ MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) {
     }
 }
 
-Rounder stem_to_object::rounder(skeleton::StemEnum stem) {
+Precision stem_to_object::precision(skeleton::StemEnum stem) {
     switch (stem) {
-        case STEM_ROUND_INTEGER:
-            return Rounder::integer();
-        case STEM_ROUND_UNLIMITED:
-            return Rounder::unlimited();
-        case STEM_ROUND_CURRENCY_STANDARD:
-            return Rounder::currency(UCURR_USAGE_STANDARD);
-        case STEM_ROUND_CURRENCY_CASH:
-            return Rounder::currency(UCURR_USAGE_CASH);
+        case STEM_PRECISION_INTEGER:
+            return Precision::integer();
+        case STEM_PRECISION_UNLIMITED:
+            return Precision::unlimited();
+        case STEM_PRECISION_CURRENCY_STANDARD:
+            return Precision::currency(UCURR_USAGE_STANDARD);
+        case STEM_PRECISION_CURRENCY_CASH:
+            return Precision::currency(UCURR_USAGE_CASH);
         default:
             U_ASSERT(false);
-            return Rounder::integer(); // return a value: silence compiler warning
+            return Precision::integer(); // return a value: silence compiler warning
+    }
+}
+
+UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem) {
+    switch (stem) {
+        case STEM_ROUNDING_MODE_CEILING:
+            return UNUM_ROUND_CEILING;
+        case STEM_ROUNDING_MODE_FLOOR:
+            return UNUM_ROUND_FLOOR;
+        case STEM_ROUNDING_MODE_DOWN:
+            return UNUM_ROUND_DOWN;
+        case STEM_ROUNDING_MODE_UP:
+            return UNUM_ROUND_UP;
+        case STEM_ROUNDING_MODE_HALF_EVEN:
+            return UNUM_ROUND_HALFEVEN;
+        case STEM_ROUNDING_MODE_HALF_DOWN:
+            return UNUM_ROUND_HALFDOWN;
+        case STEM_ROUNDING_MODE_HALF_UP:
+            return UNUM_ROUND_HALFUP;
+        case STEM_ROUNDING_MODE_UNNECESSARY:
+            return UNUM_ROUND_UNNECESSARY;
+        default:
+            U_ASSERT(false);
+            return UNUM_ROUND_UNNECESSARY;
     }
 }
 
@@ -265,6 +287,37 @@ UNumberDecimalSeparatorDisplay stem_to_object::decimalSeparatorDisplay(skeleton:
 }
 
 
+void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb) {
+    switch (value) {
+        case UNUM_ROUND_CEILING:
+            sb.append(u"rounding-mode-ceiling", -1);
+            break;
+        case UNUM_ROUND_FLOOR:
+            sb.append(u"rounding-mode-floor", -1);
+            break;
+        case UNUM_ROUND_DOWN:
+            sb.append(u"rounding-mode-down", -1);
+            break;
+        case UNUM_ROUND_UP:
+            sb.append(u"rounding-mode-up", -1);
+            break;
+        case UNUM_ROUND_HALFEVEN:
+            sb.append(u"rounding-mode-half-even", -1);
+            break;
+        case UNUM_ROUND_HALFDOWN:
+            sb.append(u"rounding-mode-half-down", -1);
+            break;
+        case UNUM_ROUND_HALFUP:
+            sb.append(u"rounding-mode-half-up", -1);
+            break;
+        case UNUM_ROUND_UNNECESSARY:
+            sb.append(u"rounding-mode-unnecessary", -1);
+            break;
+        default:
+            U_ASSERT(false);
+    }
+}
+
 void enum_to_stem_string::groupingStrategy(UGroupingStrategy value, UnicodeString& sb) {
     switch (value) {
         case UNUM_GROUPING_OFF:
@@ -442,7 +495,7 @@ MacroProps skeleton::parseSkeleton(const UnicodeString& skeletonString, UErrorCo
         // Does the current stem require an option?
         if (isTokenSeparator && stem != STATE_NULL) {
             switch (stem) {
-                case STATE_INCREMENT_ROUNDER:
+                case STATE_INCREMENT_PRECISION:
                 case STATE_MEASURE_UNIT:
                 case STATE_PER_MEASURE_UNIT:
                 case STATE_CURRENCY_UNIT:
@@ -472,11 +525,11 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
     // First check for "blueprint" stems, which start with a "signal char"
     switch (segment.charAt(0)) {
         case u'.':
-        CHECK_NULL(seen, rounder, status);
+        CHECK_NULL(seen, precision, status);
             blueprint_helpers::parseFractionStem(segment, macros, status);
-            return STATE_FRACTION_ROUNDER;
+            return STATE_FRACTION_PRECISION;
         case u'@':
-        CHECK_NULL(seen, rounder, status);
+        CHECK_NULL(seen, precision, status);
             blueprint_helpers::parseDigitsStem(segment, macros, status);
             return STATE_NULL;
         default:
@@ -519,19 +572,31 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
             macros.unit = stem_to_object::unit(stem);
             return STATE_NULL;
 
-        case STEM_ROUND_INTEGER:
-        case STEM_ROUND_UNLIMITED:
-        case STEM_ROUND_CURRENCY_STANDARD:
-        case STEM_ROUND_CURRENCY_CASH:
-        CHECK_NULL(seen, rounder, status);
-            macros.rounder = stem_to_object::rounder(stem);
+        case STEM_PRECISION_INTEGER:
+        case STEM_PRECISION_UNLIMITED:
+        case STEM_PRECISION_CURRENCY_STANDARD:
+        case STEM_PRECISION_CURRENCY_CASH:
+        CHECK_NULL(seen, precision, status);
+            macros.precision = stem_to_object::precision(stem);
             switch (stem) {
-                case STEM_ROUND_INTEGER:
-                    return STATE_FRACTION_ROUNDER; // allows for "round-integer/@##"
+                case STEM_PRECISION_INTEGER:
+                    return STATE_FRACTION_PRECISION; // allows for "precision-integer/@##"
                 default:
-                    return STATE_ROUNDER; // allows for rounding mode options
+                    return STATE_NULL;
             }
 
+        case STEM_ROUNDING_MODE_CEILING:
+        case STEM_ROUNDING_MODE_FLOOR:
+        case STEM_ROUNDING_MODE_DOWN:
+        case STEM_ROUNDING_MODE_UP:
+        case STEM_ROUNDING_MODE_HALF_EVEN:
+        case STEM_ROUNDING_MODE_HALF_DOWN:
+        case STEM_ROUNDING_MODE_HALF_UP:
+        case STEM_ROUNDING_MODE_UNNECESSARY:
+        CHECK_NULL(seen, roundingMode, status);
+            macros.roundingMode = stem_to_object::roundingMode(stem);
+            return STATE_NULL;
+
         case STEM_GROUP_OFF:
         case STEM_GROUP_MIN2:
         case STEM_GROUP_AUTO:
@@ -574,9 +639,9 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
 
             // Stems requiring an option:
 
-        case STEM_ROUND_INCREMENT:
-        CHECK_NULL(seen, rounder, status);
-            return STATE_INCREMENT_ROUNDER;
+        case STEM_PRECISION_INCREMENT:
+        CHECK_NULL(seen, precision, status);
+            return STATE_INCREMENT_PRECISION;
 
         case STEM_MEASURE_UNIT:
         CHECK_NULL(seen, unit, status);
@@ -623,9 +688,9 @@ ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment,
         case STATE_PER_MEASURE_UNIT:
             blueprint_helpers::parseMeasurePerUnitOption(segment, macros, status);
             return STATE_NULL;
-        case STATE_INCREMENT_ROUNDER:
+        case STATE_INCREMENT_PRECISION:
             blueprint_helpers::parseIncrementOption(segment, macros, status);
-            return STATE_ROUNDER;
+            return STATE_NULL;
         case STATE_INTEGER_WIDTH:
             blueprint_helpers::parseIntegerWidthOption(segment, macros, status);
             return STATE_NULL;
@@ -657,21 +722,9 @@ ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment,
 
     // Frac-sig option
     switch (stem) {
-        case STATE_FRACTION_ROUNDER:
+        case STATE_FRACTION_PRECISION:
             if (blueprint_helpers::parseFracSigOption(segment, macros, status)) {
-                return STATE_ROUNDER;
-            }
-            break;
-        default:
-            break;
-    }
-
-    // Rounding mode option
-    switch (stem) {
-        case STATE_ROUNDER:
-        case STATE_FRACTION_ROUNDER:
-            if (blueprint_helpers::parseRoundingModeOption(segment, macros, status)) {
-                return STATE_ROUNDER;
+                return STATE_NULL;
             }
             break;
         default:
@@ -698,7 +751,11 @@ void GeneratorHelpers::generateSkeleton(const MacroProps& macros, UnicodeString&
         sb.append(u' ');
     }
     if (U_FAILURE(status)) { return; }
-    if (GeneratorHelpers::rounding(macros, sb, status)) {
+    if (GeneratorHelpers::precision(macros, sb, status)) {
+        sb.append(u' ');
+    }
+    if (U_FAILURE(status)) { return; }
+    if (GeneratorHelpers::roundingMode(macros, sb, status)) {
         sb.append(u' ');
     }
     if (U_FAILURE(status)) { return; }
@@ -927,16 +984,16 @@ void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroPro
     }
     // Use the public APIs to enforce bounds checking
     if (maxFrac == -1) {
-        macros.rounder = Rounder::minFraction(minFrac);
+        macros.precision = Precision::minFraction(minFrac);
     } else {
-        macros.rounder = Rounder::minMaxFraction(minFrac, maxFrac);
+        macros.precision = Precision::minMaxFraction(minFrac, maxFrac);
     }
 }
 
 void
 blueprint_helpers::generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode&) {
     if (minFrac == 0 && maxFrac == 0) {
-        sb.append(u"round-integer", -1);
+        sb.append(u"precision-integer", -1);
         return;
     }
     sb.append(u'.');
@@ -984,9 +1041,9 @@ blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& mac
     }
     // Use the public APIs to enforce bounds checking
     if (maxSig == -1) {
-        macros.rounder = Rounder::minDigits(minSig);
+        macros.precision = Precision::minSignificantDigits(minSig);
     } else {
-        macros.rounder = Rounder::minMaxDigits(minSig, maxSig);
+        macros.precision = Precision::minMaxSignificantDigits(minSig, maxSig);
     }
 }
 
@@ -1051,11 +1108,11 @@ bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroPr
         return false;
     }
 
-    auto& oldRounder = static_cast<const FractionRounder&>(macros.rounder);
+    auto& oldPrecision = static_cast<const FractionPrecision&>(macros.precision);
     if (maxSig == -1) {
-        macros.rounder = oldRounder.withMinDigits(minSig);
+        macros.precision = oldPrecision.withMinDigits(minSig);
     } else {
-        macros.rounder = oldRounder.withMaxDigits(maxSig);
+        macros.precision = oldPrecision.withMaxDigits(maxSig);
     }
     return true;
 }
@@ -1083,10 +1140,10 @@ void blueprint_helpers::parseIncrementOption(const StringSegment& segment, Macro
         decimalOffset++;
     }
     if (decimalOffset == segment.length()) {
-        macros.rounder = Rounder::increment(increment);
+        macros.precision = Precision::increment(increment);
     } else {
         int32_t fractionLength = segment.length() - decimalOffset - 1;
-        macros.rounder = Rounder::increment(increment).withMinFraction(fractionLength);
+        macros.precision = Precision::increment(increment).withMinFraction(fractionLength);
     }
 }
 
@@ -1104,21 +1161,6 @@ void blueprint_helpers::generateIncrementOption(double increment, int32_t traili
     }
 }
 
-bool
-blueprint_helpers::parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) {
-    for (int rm = 0; rm < kRoundingModeCount; rm++) {
-        if (segment == UnicodeString(kRoundingModeStrings[rm], -1)) {
-            macros.rounder = macros.rounder.withMode(static_cast<RoundingMode>(rm));
-            return true;
-        }
-    }
-    return false;
-}
-
-void blueprint_helpers::generateRoundingModeOption(RoundingMode mode, UnicodeString& sb, UErrorCode&) {
-    sb.append(kRoundingModeStrings[mode], -1);
-}
-
 void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros,
                                                 UErrorCode& status) {
     int32_t offset = 0;
@@ -1307,17 +1349,17 @@ bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErr
     }
 }
 
-bool GeneratorHelpers::rounding(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
-    if (macros.rounder.fType == Rounder::RND_NONE) {
-        sb.append(u"round-unlimited", -1);
-    } else if (macros.rounder.fType == Rounder::RND_FRACTION) {
-        const Rounder::FractionSignificantSettings& impl = macros.rounder.fUnion.fracSig;
+bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
+    if (macros.precision.fType == Precision::RND_NONE) {
+        sb.append(u"precision-unlimited", -1);
+    } else if (macros.precision.fType == Precision::RND_FRACTION) {
+        const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
         blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status);
-    } else if (macros.rounder.fType == Rounder::RND_SIGNIFICANT) {
-        const Rounder::FractionSignificantSettings& impl = macros.rounder.fUnion.fracSig;
+    } else if (macros.precision.fType == Precision::RND_SIGNIFICANT) {
+        const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
         blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status);
-    } else if (macros.rounder.fType == Rounder::RND_FRACTION_SIGNIFICANT) {
-        const Rounder::FractionSignificantSettings& impl = macros.rounder.fUnion.fracSig;
+    } else if (macros.precision.fType == Precision::RND_FRACTION_SIGNIFICANT) {
+        const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
         blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status);
         sb.append(u'/');
         if (impl.fMinSig == -1) {
@@ -1325,36 +1367,38 @@ bool GeneratorHelpers::rounding(const MacroProps& macros, UnicodeString& sb, UEr
         } else {
             blueprint_helpers::generateDigitsStem(impl.fMinSig, -1, sb, status);
         }
-    } else if (macros.rounder.fType == Rounder::RND_INCREMENT) {
-        const Rounder::IncrementSettings& impl = macros.rounder.fUnion.increment;
-        sb.append(u"round-increment/", -1);
+    } else if (macros.precision.fType == Precision::RND_INCREMENT) {
+        const Precision::IncrementSettings& impl = macros.precision.fUnion.increment;
+        sb.append(u"precision-increment/", -1);
         blueprint_helpers::generateIncrementOption(
                 impl.fIncrement,
                 impl.fMinFrac - impl.fMaxFrac,
                 sb,
                 status);
-    } else if (macros.rounder.fType == Rounder::RND_CURRENCY) {
-        UCurrencyUsage usage = macros.rounder.fUnion.currencyUsage;
+    } else if (macros.precision.fType == Precision::RND_CURRENCY) {
+        UCurrencyUsage usage = macros.precision.fUnion.currencyUsage;
         if (usage == UCURR_USAGE_STANDARD) {
-            sb.append(u"round-currency-standard", -1);
+            sb.append(u"precision-currency-standard", -1);
         } else {
-            sb.append(u"round-currency-cash", -1);
+            sb.append(u"precision-currency-cash", -1);
         }
     } else {
         // Bogus or Error
         return false;
     }
 
-    // Generate the options
-    if (macros.rounder.fRoundingMode != kDefaultMode) {
-        sb.append(u'/');
-        blueprint_helpers::generateRoundingModeOption(macros.rounder.fRoundingMode, sb, status);
-    }
-
     // NOTE: Always return true for rounding because the default value depends on other options.
     return true;
 }
 
+bool GeneratorHelpers::roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
+    if (macros.roundingMode == kDefaultMode) {
+        return false; // Default
+    }
+    enum_to_stem_string::roundingMode(macros.roundingMode, sb);
+    return true;
+}
+
 bool GeneratorHelpers::grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
     if (macros.grouper.isBogus()) {
         return false; // No value
diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h
index b84f47cc327..3e4def26527 100644
--- a/icu4c/source/i18n/number_skeletons.h
+++ b/icu4c/source/i18n/number_skeletons.h
@@ -39,12 +39,11 @@ enum ParseState {
     // Section 1: We might accept an option, but it is not required:
 
     STATE_SCIENTIFIC,
-    STATE_ROUNDER,
-    STATE_FRACTION_ROUNDER,
+    STATE_FRACTION_PRECISION,
 
     // Section 2: An option is required:
 
-    STATE_INCREMENT_ROUNDER,
+    STATE_INCREMENT_PRECISION,
     STATE_MEASURE_UNIT,
     STATE_PER_MEASURE_UNIT,
     STATE_CURRENCY_UNIT,
@@ -72,10 +71,18 @@ enum StemEnum {
     STEM_BASE_UNIT,
     STEM_PERCENT,
     STEM_PERMILLE,
-    STEM_ROUND_INTEGER,
-    STEM_ROUND_UNLIMITED,
-    STEM_ROUND_CURRENCY_STANDARD,
-    STEM_ROUND_CURRENCY_CASH,
+    STEM_PRECISION_INTEGER,
+    STEM_PRECISION_UNLIMITED,
+    STEM_PRECISION_CURRENCY_STANDARD,
+    STEM_PRECISION_CURRENCY_CASH,
+    STEM_ROUNDING_MODE_CEILING,
+    STEM_ROUNDING_MODE_FLOOR,
+    STEM_ROUNDING_MODE_DOWN,
+    STEM_ROUNDING_MODE_UP,
+    STEM_ROUNDING_MODE_HALF_EVEN,
+    STEM_ROUNDING_MODE_HALF_DOWN,
+    STEM_ROUNDING_MODE_HALF_UP,
+    STEM_ROUNDING_MODE_UNNECESSARY,
     STEM_GROUP_OFF,
     STEM_GROUP_MIN2,
     STEM_GROUP_AUTO,
@@ -99,7 +106,7 @@ enum StemEnum {
 
     // Section 2: Stems that DO require an option:
 
-    STEM_ROUND_INCREMENT,
+    STEM_PRECISION_INCREMENT,
     STEM_MEASURE_UNIT,
     STEM_PER_MEASURE_UNIT,
     STEM_CURRENCY,
@@ -163,7 +170,9 @@ Notation notation(skeleton::StemEnum stem);
 
 MeasureUnit unit(skeleton::StemEnum stem);
 
-Rounder rounder(skeleton::StemEnum stem);
+Precision precision(skeleton::StemEnum stem);
+
+UNumberFormatRoundingMode roundingMode(skeleton::StemEnum stem);
 
 UGroupingStrategy groupingStrategy(skeleton::StemEnum stem);
 
@@ -181,6 +190,8 @@ UNumberDecimalSeparatorDisplay decimalSeparatorDisplay(skeleton::StemEnum stem);
  */
 namespace enum_to_stem_string {
 
+void roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb);
+
 void groupingStrategy(UGroupingStrategy value, UnicodeString& sb);
 
 void unitWidth(UNumberUnitWidth value, UnicodeString& sb);
@@ -230,11 +241,6 @@ void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErr
 void
 generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, UErrorCode& status);
 
-/** @return Whether we successfully found and parsed a rounding mode. */
-bool parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
-
-void generateRoundingModeOption(RoundingMode mode, UnicodeString& sb, UErrorCode& status);
-
 void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
 
 void generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, UErrorCode& status);
@@ -273,7 +279,9 @@ class GeneratorHelpers {
 
     static bool perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
 
-    static bool rounding(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
+    static bool precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
+
+    static bool roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
 
     static bool grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
 
@@ -299,7 +307,8 @@ struct SeenMacroProps {
     bool notation = false;
     bool unit = false;
     bool perUnit = false;
-    bool rounder = false;
+    bool precision = false;
+    bool roundingMode = false;
     bool grouper = false;
     bool padder = false;
     bool integerWidth = false;
diff --git a/icu4c/source/i18n/number_utils.h b/icu4c/source/i18n/number_utils.h
index 2fdbb68b40b..df6f0d3080f 100644
--- a/icu4c/source/i18n/number_utils.h
+++ b/icu4c/source/i18n/number_utils.h
@@ -14,6 +14,7 @@
 #include "number_patternstring.h"
 #include "number_modifiers.h"
 #include "number_multiplier.h"
+#include "number_roundingutils.h"
 #include "decNumber.h"
 #include "charstr.h"
 
@@ -23,7 +24,7 @@ namespace impl {
 struct MicroProps : public MicroPropsGenerator {
 
     // NOTE: All of these fields are properly initialized in NumberFormatterImpl.
-    Rounder rounding;
+    RoundingImpl rounder;
     Grouper grouping;
     Padder padding;
     IntegerWidth integerWidth;
diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h
index 5ec10182234..6d709c4bf76 100644
--- a/icu4c/source/i18n/unicode/numberformatter.h
+++ b/icu4c/source/i18n/unicode/numberformatter.h
@@ -33,11 +33,11 @@
  * // Most basic usage:
  * NumberFormatter::withLocale(...).format(123).toString();  // 1,234 in en-US
  *
- * // Custom notation, unit, and rounding strategy:
+ * // Custom notation, unit, and rounding precision:
  * NumberFormatter::with()
  *     .notation(Notation::compactShort())
  *     .unit(CurrencyUnit("EUR", status))
- *     .rounding(Rounder::maxDigits(2))
+ *     .precision(PrecisionPrecision:::maxDigits(2))
  *     .locale(...)
  *     .format(1234)
  *     .toString();  // €1.2K in en-US
@@ -45,7 +45,7 @@
  * // Create a formatter in a singleton for use later:
  * static const LocalizedNumberFormatter formatter = NumberFormatter::withLocale(...)
  *     .unit(NoUnit::percent())
- *     .rounding(Rounder::fixedFraction(3));
+ *     .precision(PrecisionPrecision:::fixedFraction(3));
  * formatter.format(5.9831).toString();  // 5.983% in en-US
  *
  * // Create a "template" in a singleton but without setting a locale until the call site:
@@ -65,7 +65,7 @@
  *
  * <pre>
  * UnlocalizedNumberFormatter formatter = UnlocalizedNumberFormatter::with().notation(Notation::scientific());
- * formatter.rounding(Rounder.maxFraction(2)); // does nothing!
+ * formatter.precision(Precision.maxFraction(2)); // does nothing!
  * formatter.locale(Locale.getEnglish()).format(9.8765).toString(); // prints "9.8765E0", not "9.88E0"
  * </pre>
  *
@@ -99,10 +99,10 @@ class LocalizedNumberFormatter;
 class FormattedNumber;
 class Notation;
 class ScientificNotation;
-class Rounder;
-class FractionRounder;
-class CurrencyRounder;
-class IncrementRounder;
+class Precision;
+class FractionPrecision;
+class CurrencyPrecision;
+class IncrementPrecision;
 class IntegerWidth;
 
 namespace impl {
@@ -134,10 +134,8 @@ class NumberFormatterImpl;
 struct ParsedPatternInfo;
 class ScientificModifier;
 class MultiplierProducer;
-class MutablePatternModifier;
-class LongNameHandler;
+class RoundingImpl;
 class ScientificHandler;
-class CompactHandler;
 class Modifier;
 class NumberStringBuilder;
 class AffixPatternProvider;
@@ -240,13 +238,13 @@ class U_I18N_API Notation : public UMemory {
      * </pre>
      *
      * <p>
-     * When compact notation is specified without an explicit rounding strategy, numbers are rounded off to the closest
+     * When compact notation is specified without an explicit rounding precision, numbers are rounded off to the closest
      * integer after scaling the number by the corresponding power of 10, but with a digit shown after the decimal
-     * separator if there is only one digit before the decimal separator. The default compact notation rounding strategy
+     * separator if there is only one digit before the decimal separator. The default compact notation rounding precision
      * is equivalent to:
      *
      * <pre>
-     * Rounder.integer().withMinDigits(2)
+     * Precision::integer().withMinDigits(2)
      * </pre>
      *
      * @return A CompactNotation for passing to the NumberFormatter notation() setter.
@@ -411,17 +409,25 @@ class U_I18N_API ScientificNotation : public Notation {
 };
 
 // Reserve extra names in case they are added as classes in the future:
-typedef Rounder DigitRounder;
+typedef Precision SignificantDigitsPrecision;
+
+// Typedefs for ICU 60/61 compatibility.
+// These will be removed in ICU 64.
+// See http://bugs.icu-project.org/trac/ticket/13746
+typedef Precision Rounder;
+typedef FractionPrecision FractionRounder;
+typedef IncrementPrecision IncrementRounder;
+typedef CurrencyPrecision CurrencyRounder;
 
 /**
- * A class that defines the rounding strategy to be used when formatting numbers in NumberFormatter.
+ * A class that defines the rounding precision to be used when formatting numbers in NumberFormatter.
  *
  * <p>
- * To create a Rounder, use one of the factory methods.
+ * To create a Precision, use one of the factory methods.
  *
  * @draft ICU 60
  */
-class U_I18N_API Rounder : public UMemory {
+class U_I18N_API Precision : public UMemory {
 
   public:
     /**
@@ -437,18 +443,18 @@ class U_I18N_API Rounder : public UMemory {
      * <p>
      * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/
      *
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A Precision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static Rounder unlimited();
+    static Precision unlimited();
 
     /**
      * Show numbers rounded if necessary to the nearest integer.
      *
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static FractionRounder integer();
+    static FractionPrecision integer();
 
     /**
      * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
@@ -474,10 +480,10 @@ class U_I18N_API Rounder : public UMemory {
      * @param minMaxFractionPlaces
      *            The minimum and maximum number of numerals to display after the decimal separator (rounding if too
      *            long or padding with zeros if too short).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static FractionRounder fixedFraction(int32_t minMaxFractionPlaces);
+    static FractionPrecision fixedFraction(int32_t minMaxFractionPlaces);
 
     /**
      * Always show at least a certain number of fraction places after the decimal separator, padding with zeros if
@@ -489,10 +495,10 @@ class U_I18N_API Rounder : public UMemory {
      * @param minFractionPlaces
      *            The minimum number of numerals to display after the decimal separator (padding with zeros if
      *            necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static FractionRounder minFraction(int32_t minFractionPlaces);
+    static FractionPrecision minFraction(int32_t minFractionPlaces);
 
     /**
      * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
@@ -501,10 +507,10 @@ class U_I18N_API Rounder : public UMemory {
      *
      * @param maxFractionPlaces
      *            The maximum number of numerals to display after the decimal mark (rounding if necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static FractionRounder maxFraction(int32_t maxFractionPlaces);
+    static FractionPrecision maxFraction(int32_t maxFractionPlaces);
 
     /**
      * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator);
@@ -516,10 +522,10 @@ class U_I18N_API Rounder : public UMemory {
      *            necessary).
      * @param maxFractionPlaces
      *            The maximum number of numerals to display after the decimal separator (rounding if necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static FractionRounder minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces);
+    static FractionPrecision minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces);
 
     /**
      * Show numbers rounded if necessary to a certain number of significant digits or significant figures. Additionally,
@@ -531,10 +537,10 @@ class U_I18N_API Rounder : public UMemory {
      * @param minMaxSignificantDigits
      *            The minimum and maximum number of significant digits to display (rounding if too long or padding with
      *            zeros if too short).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
+     * @draft ICU 62
      */
-    static DigitRounder fixedDigits(int32_t minMaxSignificantDigits);
+    static SignificantDigitsPrecision fixedSignificantDigits(int32_t minMaxSignificantDigits);
 
     /**
      * Always show at least a certain number of significant digits/figures, padding with zeros if necessary. Do not
@@ -545,20 +551,20 @@ class U_I18N_API Rounder : public UMemory {
      *
      * @param minSignificantDigits
      *            The minimum number of significant digits to display (padding with zeros if too short).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
+     * @draft ICU 62
      */
-    static DigitRounder minDigits(int32_t minSignificantDigits);
+    static SignificantDigitsPrecision minSignificantDigits(int32_t minSignificantDigits);
 
     /**
      * Show numbers rounded if necessary to a certain number of significant digits/figures.
      *
      * @param maxSignificantDigits
      *            The maximum number of significant digits to display (rounding if too long).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
+     * @draft ICU 62
      */
-    static DigitRounder maxDigits(int32_t maxSignificantDigits);
+    static SignificantDigitsPrecision maxSignificantDigits(int32_t maxSignificantDigits);
 
     /**
      * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition, always show at
@@ -568,10 +574,34 @@ class U_I18N_API Rounder : public UMemory {
      *            The minimum number of significant digits to display (padding with zeros if necessary).
      * @param maxSignificantDigits
      *            The maximum number of significant digits to display (rounding if necessary).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
+     * @draft ICU 62
      */
-    static DigitRounder minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits);
+    static SignificantDigitsPrecision minMaxSignificantDigits(int32_t minSignificantDigits,
+                                                              int32_t maxSignificantDigits);
+
+    // Compatiblity methods that will be removed in ICU 64.
+    // See http://bugs.icu-project.org/trac/ticket/13746
+
+    /** @deprecated ICU 62 */
+    static inline SignificantDigitsPrecision fixedDigits(int32_t a) {
+        return fixedSignificantDigits(a);
+    }
+
+    /** @deprecated ICU 62 */
+    static inline SignificantDigitsPrecision minDigits(int32_t a) {
+        return minSignificantDigits(a);
+    }
+
+    /** @deprecated ICU 62 */
+    static inline SignificantDigitsPrecision maxDigits(int32_t a) {
+        return maxSignificantDigits(a);
+    }
+
+    /** @deprecated ICU 62 */
+    static inline SignificantDigitsPrecision minMaxDigits(int32_t a, int32_t b) {
+        return minMaxSignificantDigits(a, b);
+    }
 
     /**
      * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For example, if the
@@ -584,20 +614,20 @@ class U_I18N_API Rounder : public UMemory {
      * decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you can run:
      *
      * <pre>
-     * Rounder::increment(0.5).withMinFraction(2)
+     * Precision::increment(0.5).withMinFraction(2)
      * </pre>
      *
      * @param roundingIncrement
      *            The increment to which to round numbers.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static IncrementRounder increment(double roundingIncrement);
+    static IncrementPrecision increment(double roundingIncrement);
 
     /**
      * Show numbers rounded and padded according to the rules for the currency unit. The most common rounding settings
-     * for currencies include <code>Rounder.fixedFraction(2)</code>, <code>Rounder.integer()</code>, and
-     * <code>Rounder.increment(0.05)</code> for cash transactions ("nickel rounding").
+     * for currencies include <code>Precision::fixedFraction(2)</code>, <code>Precision::integer()</code>, and
+     * <code>Precision::increment(0.05)</code> for cash transactions ("nickel rounding").
      *
      * <p>
      * The exact rounding details will be resolved at runtime based on the currency unit specified in the
@@ -607,10 +637,10 @@ class U_I18N_API Rounder : public UMemory {
      * @param currencyUsage
      *            Either STANDARD (for digital transactions) or CASH (for transactions where the rounding increment may
      *            be limited by the available denominations of cash or coins).
-     * @return A CurrencyRounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A CurrencyPrecision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    static CurrencyRounder currency(UCurrencyUsage currencyUsage);
+    static CurrencyPrecision currency(UCurrencyUsage currencyUsage);
 
     /**
      * Sets the rounding mode to use when picking the direction to round (up or down). Common values
@@ -618,13 +648,15 @@ class U_I18N_API Rounder : public UMemory {
      *
      * @param roundingMode
      *            The RoundingMode to use.
-     * @return A Rounder for passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
+     * @return A Precision for passing to the NumberFormatter precision() setter.
+     * @deprecated ICU 62 Use the top-level roundingMode() setting instead.
+     *            This method will be removed in ICU 64.
+     *            See http://bugs.icu-project.org/trac/ticket/13746
      */
-    Rounder withMode(UNumberFormatRoundingMode roundingMode) const;
+    Precision withMode(UNumberFormatRoundingMode roundingMode) const;
 
   private:
-    enum RounderType {
+    enum PrecisionType {
         RND_BOGUS,
         RND_NONE,
         RND_FRACTION,
@@ -632,11 +664,10 @@ class U_I18N_API Rounder : public UMemory {
         RND_FRACTION_SIGNIFICANT,
         RND_INCREMENT,
         RND_CURRENCY,
-        RND_PASS_THROUGH,
         RND_ERROR
     } fType;
 
-    union RounderUnion {
+    union PrecisionUnion {
         struct FractionSignificantSettings {
             // For RND_FRACTION, RND_SIGNIFICANT, and RND_FRACTION_SIGNIFICANT
             impl::digits_t fMinFrac;
@@ -653,19 +684,21 @@ class U_I18N_API Rounder : public UMemory {
         UErrorCode errorCode; // For RND_ERROR
     } fUnion;
 
-    typedef RounderUnion::FractionSignificantSettings FractionSignificantSettings;
-    typedef RounderUnion::IncrementSettings IncrementSettings;
+    typedef PrecisionUnion::FractionSignificantSettings FractionSignificantSettings;
+    typedef PrecisionUnion::IncrementSettings IncrementSettings;
 
+    /** The Precision encapsulates the RoundingMode when used within the implementation. */
     UNumberFormatRoundingMode fRoundingMode;
 
-    Rounder(const RounderType &type, const RounderUnion &union_, UNumberFormatRoundingMode roundingMode)
+    Precision(const PrecisionType& type, const PrecisionUnion& union_,
+              UNumberFormatRoundingMode roundingMode)
             : fType(type), fUnion(union_), fRoundingMode(roundingMode) {}
 
-    Rounder(UErrorCode errorCode) : fType(RND_ERROR) {
+    Precision(UErrorCode errorCode) : fType(RND_ERROR) {
         fUnion.errorCode = errorCode;
     }
 
-    Rounder() : fType(RND_BOGUS) {}
+    Precision() : fType(RND_BOGUS) {}
 
     bool isBogus() const {
         return fType == RND_BOGUS;
@@ -679,47 +712,21 @@ class U_I18N_API Rounder : public UMemory {
         return FALSE;
     }
 
-    // On the parent type so that this method can be called internally on Rounder instances.
-    Rounder withCurrency(const CurrencyUnit &currency, UErrorCode &status) const;
+    // On the parent type so that this method can be called internally on Precision instances.
+    Precision withCurrency(const CurrencyUnit &currency, UErrorCode &status) const;
 
-    /** NON-CONST: mutates the current instance. */
-    void setLocaleData(const CurrencyUnit &currency, UErrorCode &status);
+    static FractionPrecision constructFraction(int32_t minFrac, int32_t maxFrac);
 
-    void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
+    static Precision constructSignificant(int32_t minSig, int32_t maxSig);
 
-    /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
-    void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
+    static Precision
+    constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig);
 
-    /**
-     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
-     * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
-     *
-     * <p>
-     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
-     * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
-     * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
-     * change your multiplier to be -6, and you get 1.0E6, which is correct.
-     *
-     * @param input The quantity to process.
-     * @param producer Function to call to return a multiplier based on a magnitude.
-     * @return The number of orders of magnitude the input was adjusted by this method.
-     */
-    int32_t
-    chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
-                             UErrorCode &status);
+    static IncrementPrecision constructIncrement(double increment, int32_t minFrac);
 
-    static FractionRounder constructFraction(int32_t minFrac, int32_t maxFrac);
+    static CurrencyPrecision constructCurrency(UCurrencyUsage usage);
 
-    static Rounder constructSignificant(int32_t minSig, int32_t maxSig);
-
-    static Rounder
-    constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig);
-
-    static IncrementRounder constructIncrement(double increment, int32_t minFrac);
-
-    static CurrencyRounder constructCurrency(UCurrencyUsage usage);
-
-    static Rounder constructPassThrough();
+    static Precision constructPassThrough();
 
     // To allow MacroProps/MicroProps to initialize bogus instances:
     friend struct impl::MacroProps;
@@ -731,31 +738,28 @@ class U_I18N_API Rounder : public UMemory {
     // To allow NumberPropertyMapper to create instances from DecimalFormatProperties:
     friend class impl::NumberPropertyMapper;
 
-    // To give access to apply() and chooseMultiplierAndApply():
-    friend class impl::MutablePatternModifier;
-    friend class impl::LongNameHandler;
-    friend class impl::ScientificHandler;
-    friend class impl::CompactHandler;
+    // To allow access to the main implementation class:
+    friend class impl::RoundingImpl;
 
     // To allow child classes to call private methods:
-    friend class FractionRounder;
-    friend class CurrencyRounder;
-    friend class IncrementRounder;
+    friend class FractionPrecision;
+    friend class CurrencyPrecision;
+    friend class IncrementPrecision;
 
     // To allow access to the skeleton generation code:
     friend class impl::GeneratorHelpers;
 };
 
 /**
- * A class that defines a rounding strategy based on a number of fraction places and optionally significant digits to be
+ * A class that defines a rounding precision based on a number of fraction places and optionally significant digits to be
  * used when formatting numbers in NumberFormatter.
  *
  * <p>
- * To create a FractionRounder, use one of the factory methods on Rounder.
+ * To create a FractionPrecision, use one of the factory methods on Precision.
  *
  * @draft ICU 60
  */
-class U_I18N_API FractionRounder : public Rounder {
+class U_I18N_API FractionPrecision : public Precision {
   public:
     /**
      * Ensure that no less than this number of significant digits are retained when rounding according to fraction
@@ -770,10 +774,10 @@ class U_I18N_API FractionRounder : public Rounder {
      *
      * @param minSignificantDigits
      *            The number of significant figures to guarantee.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    Rounder withMinDigits(int32_t minSignificantDigits) const;
+    Precision withMinDigits(int32_t minSignificantDigits) const;
 
     /**
      * Ensure that no more than this number of significant digits are retained when rounding according to fraction
@@ -789,36 +793,36 @@ class U_I18N_API FractionRounder : public Rounder {
      *
      * @param maxSignificantDigits
      *            Round the number to no more than this number of significant figures.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    Rounder withMaxDigits(int32_t maxSignificantDigits) const;
+    Precision withMaxDigits(int32_t maxSignificantDigits) const;
 
   private:
     // Inherit constructor
-    using Rounder::Rounder;
+    using Precision::Precision;
 
     // To allow parent class to call this class's constructor:
-    friend class Rounder;
+    friend class Precision;
 };
 
 /**
- * A class that defines a rounding strategy parameterized by a currency to be used when formatting numbers in
+ * A class that defines a rounding precision parameterized by a currency to be used when formatting numbers in
  * NumberFormatter.
  *
  * <p>
- * To create a CurrencyRounder, use one of the factory methods on Rounder.
+ * To create a CurrencyPrecision, use one of the factory methods on Precision.
  *
  * @draft ICU 60
  */
-class U_I18N_API CurrencyRounder : public Rounder {
+class U_I18N_API CurrencyPrecision : public Precision {
   public:
     /**
-      * Associates a currency with this rounding strategy.
+      * Associates a currency with this rounding precision.
       *
       * <p>
       * <strong>Calling this method is <em>not required</em></strong>, because the currency specified in unit()
-      * is automatically applied to currency rounding strategies. However,
+      * is automatically applied to currency rounding precisions. However,
       * this method enables you to override that automatic association.
       *
       * <p>
@@ -826,30 +830,30 @@ class U_I18N_API CurrencyRounder : public Rounder {
       * currency format.
       *
       * @param currency
-      *            The currency to associate with this rounding strategy.
-      * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+      *            The currency to associate with this rounding precision.
+      * @return A precision for chaining or passing to the NumberFormatter precision() setter.
       * @draft ICU 60
       */
-    Rounder withCurrency(const CurrencyUnit &currency) const;
+    Precision withCurrency(const CurrencyUnit &currency) const;
 
   private:
     // Inherit constructor
-    using Rounder::Rounder;
+    using Precision::Precision;
 
     // To allow parent class to call this class's constructor:
-    friend class Rounder;
+    friend class Precision;
 };
 
 /**
- * A class that defines a rounding strategy parameterized by a rounding increment to be used when formatting numbers in
+ * A class that defines a rounding precision parameterized by a rounding increment to be used when formatting numbers in
  * NumberFormatter.
  *
  * <p>
- * To create an IncrementRounder, use one of the factory methods on Rounder.
+ * To create an IncrementPrecision, use one of the factory methods on Precision.
  *
  * @draft ICU 60
  */
-class U_I18N_API IncrementRounder : public Rounder {
+class U_I18N_API IncrementPrecision : public Precision {
   public:
     /**
      * Specifies the minimum number of fraction digits to render after the decimal separator, padding with zeros if
@@ -863,17 +867,17 @@ class U_I18N_API IncrementRounder : public Rounder {
      * Note: In ICU4J, this functionality is accomplished via the scale of the BigDecimal rounding increment.
      *
      * @param minFrac The minimum number of digits after the decimal separator.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+     * @return A precision for chaining or passing to the NumberFormatter precision() setter.
      * @draft ICU 60
      */
-    Rounder withMinFraction(int32_t minFrac) const;
+    Precision withMinFraction(int32_t minFrac) const;
 
   private:
     // Inherit constructor
-    using Rounder::Rounder;
+    using Precision::Precision;
 
     // To allow parent class to call this class's constructor:
-    friend class Rounder;
+    friend class Precision;
 };
 
 /**
@@ -1350,7 +1354,10 @@ struct U_I18N_API MacroProps : public UMemory {
     MeasureUnit perUnit; // = NoUnit::base();
 
     /** @internal */
-    Rounder rounder;  // = Rounder();  (bogus)
+    Precision precision;  // = Precision();  (bogus)
+
+    /** @internal */
+    UNumberFormatRoundingMode roundingMode = UNUM_ROUND_HALFEVEN;
 
     /** @internal */
     Grouper grouper;  // = Grouper();  (bogus)
@@ -1400,7 +1407,7 @@ struct U_I18N_API MacroProps : public UMemory {
      * @internal
      */
     bool copyErrorTo(UErrorCode &status) const {
-        return notation.copyErrorTo(status) || rounder.copyErrorTo(status) ||
+        return notation.copyErrorTo(status) || precision.copyErrorTo(status) ||
                padder.copyErrorTo(status) || integerWidth.copyErrorTo(status) ||
                symbols.copyErrorTo(status) || scale.copyErrorTo(status);
     }
@@ -1426,7 +1433,7 @@ class U_I18N_API NumberFormatterSettings {
      *
      * <p>
      * All notation styles will be properly localized with locale data, and all notation styles are compatible with
-     * units, rounding strategies, and other number formatter settings.
+     * units, rounding precisions, and other number formatter settings.
      *
      * <p>
      * Pass this method the return value of a {@link Notation} factory method. For example:
@@ -1466,7 +1473,7 @@ class U_I18N_API NumberFormatterSettings {
      * </ul>
      *
      * All units will be properly localized with locale data, and all units are compatible with notation styles,
-     * rounding strategies, and other number formatter settings.
+     * rounding precisions, and other number formatter settings.
      *
      * Pass this method any instance of {@link MeasureUnit}. For units of measure (which often involve the
      * factory methods that return a pointer):
@@ -1602,7 +1609,7 @@ class U_I18N_API NumberFormatterSettings {
     Derived adoptPerUnit(icu::MeasureUnit *perUnit) &&;
 
     /**
-     * Specifies the rounding strategy to use when formatting numbers.
+     * Specifies the rounding precision to use when formatting numbers.
      *
      * <ul>
      * <li>Round to 3 decimal places: "3.142"
@@ -1612,37 +1619,75 @@ class U_I18N_API NumberFormatterSettings {
      * </ul>
      *
      * <p>
-     * Pass this method the return value of one of the factory methods on {@link Rounder}. For example:
+     * Pass this method the return value of one of the factory methods on {@link Precision}. For example:
      *
      * <pre>
-     * NumberFormatter::with().rounding(Rounder::fixedFraction(2))
+     * NumberFormatter::with().precision(Precision::fixedFraction(2))
      * </pre>
      *
      * <p>
      * In most cases, the default rounding strategy is to round to 6 fraction places; i.e.,
-     * <code>Rounder.maxFraction(6)</code>. The exceptions are if compact notation is being used, then the compact
+     * <code>Precision.maxFraction(6)</code>. The exceptions are if compact notation is being used, then the compact
      * notation rounding strategy is used (see {@link Notation#compactShort} for details), or if the unit is a currency,
-     * then standard currency rounding is used, which varies from currency to currency (see {@link Rounder#currency} for
+     * then standard currency rounding is used, which varies from currency to currency (see {@link Precision#currency} for
      * details).
      *
-     * @param rounder
-     *            The rounding strategy to use.
+     * @param precision
+     *            The rounding precision to use.
      * @return The fluent chain.
-     * @see Rounder
+     * @see Precision
      * @draft ICU 60
      */
-    Derived rounding(const Rounder &rounder) const &;
+    Derived precision(const Precision& precision) const &;
 
     /**
-     * Overload of rounding() for use on an rvalue reference.
+     * Overload of precision() for use on an rvalue reference.
      *
-     * @param rounder
-     *            The rounding strategy to use.
+     * @param precision
+     *            The rounding precision to use.
      * @return The fluent chain.
-     * @see #rounding
+     * @see #precision
      * @draft ICU 62
      */
-    Derived rounding(const Rounder& rounder) &&;
+    Derived precision(const Precision& precision) &&;
+
+    // Compatibility method that will be removed in ICU 64.
+    // Use precision() instead.
+    // See http://bugs.icu-project.org/trac/ticket/13746
+    /** @deprecated ICU 62 */
+    Derived rounding(const Rounder& rounder) const & {
+        return precision(rounder);
+    }
+
+    /**
+     * Specifies how to determine the direction to round a number when it has more digits than fit in the
+     * desired precision.  When formatting 1.235:
+     *
+     * <ul>
+     * <li>Ceiling rounding mode with integer precision: "2"
+     * <li>Half-down rounding mode with 2 fixed fraction digits: "1.23"
+     * <li>Half-up rounding mode with 2 fixed fraction digits: "1.24"
+     * </ul>
+     *
+     * The default is HALF_EVEN. For more information on rounding mode, see the ICU userguide here:
+     *
+     * http://userguide.icu-project.org/formatparse/numbers/rounding-modes
+     *
+     * @param roundingMode The rounding mode to use.
+     * @return The fluent chain.
+     * @draft ICU 62
+     */
+    Derived roundingMode(UNumberFormatRoundingMode roundingMode) const &;
+
+    /**
+     * Overload of roundingMode() for use on an rvalue reference.
+     *
+     * @param roundingMode The rounding mode to use.
+     * @return The fluent chain.
+     * @see #roundingMode
+     * @draft ICU 62
+     */
+    Derived roundingMode(UNumberFormatRoundingMode roundingMode) &&;
 
     /**
      * Specifies the grouping strategy to use when formatting numbers.
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index e73ab827bbc..2212f8481d0 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -30,7 +30,7 @@
  * <pre>
  * // Setup:
  * UErrorCode ec = U_ZERO_ERROR;
- * UNumberFormatter* uformatter = unumf_openFromSkeletonAndLocale(u"round-integer", -1, "en", &ec);
+ * UNumberFormatter* uformatter = unumf_openFromSkeletonAndLocale(u"precision-integer", -1, "en", &ec);
  * UFormattedNumber* uresult = unumf_openResult(&ec);
  * if (U_FAILURE(ec)) { return; }
  *
@@ -423,7 +423,7 @@ typedef struct UFormattedNumber UFormattedNumber;
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
- * @param skeleton The skeleton string, like u"percent round-integer"
+ * @param skeleton The skeleton string, like u"percent precision-integer"
  * @param skeletonLen The number of UChars in the skeleton string, or -1 it it is NUL-terminated.
  * @param locale The NUL-terminated locale ID.
  * @param ec Set if an error occurs.
diff --git a/icu4c/source/test/cintltst/unumberformattertst.c b/icu4c/source/test/cintltst/unumberformattertst.c
index 0b35c463328..a8d2fc7f585 100644
--- a/icu4c/source/test/cintltst/unumberformattertst.c
+++ b/icu4c/source/test/cintltst/unumberformattertst.c
@@ -38,7 +38,7 @@ static void TestSkeletonFormatToString() {
 
     // setup:
     UNumberFormatter* f = unumf_openFromSkeletonAndLocale(
-            u"round-integer currency/USD sign-accounting", -1, "en", &ec);
+            u"precision-integer currency/USD sign-accounting", -1, "en", &ec);
     assertSuccess("Should create without error", &ec);
     UFormattedNumber* result = unumf_openResult(&ec);
     assertSuccess("Should create result without error", &ec);
@@ -153,7 +153,7 @@ static void TestExampleCode() {
 
     // Setup:
     UErrorCode ec = U_ZERO_ERROR;
-    UNumberFormatter* uformatter = unumf_openFromSkeletonAndLocale(u"round-integer", -1, "en", &ec);
+    UNumberFormatter* uformatter = unumf_openFromSkeletonAndLocale(u"precision-integer", -1, "en", &ec);
     UFormattedNumber* uresult = unumf_openResult(&ec);
     assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
     if (U_FAILURE(ec)) { return; }
diff --git a/icu4c/source/test/intltest/numbertest_affixutils.cpp b/icu4c/source/test/intltest/numbertest_affixutils.cpp
index 1815f8ed991..d72991a042f 100644
--- a/icu4c/source/test/intltest/numbertest_affixutils.cpp
+++ b/icu4c/source/test/intltest/numbertest_affixutils.cpp
@@ -18,7 +18,7 @@ class DefaultSymbolProvider : public SymbolProvider {
   public:
     DefaultSymbolProvider(UErrorCode &status) : fSymbols(Locale("ar_SA"), status) {}
 
-    virtual UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE {
+    UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE {
         switch (type) {
             case TYPE_MINUS_SIGN:
                 return u"−";
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 4947af33a7f..95a5114ea31 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -533,7 +533,7 @@ void NumberFormatterApiTest::unitMeasure() {
     assertFormatSingle(
             u"MeasureUnit form without {0} in CLDR pattern and wide base form",
             u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
-            NumberFormatter::with().rounding(Rounder::fixedFraction(20))
+            NumberFormatter::with().precision(Precision::fixedFraction(20))
                     .unit(KELVIN)
                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
             Locale("es-MX"),
@@ -792,8 +792,8 @@ void NumberFormatterApiTest::unitPercent() {
 void NumberFormatterApiTest::roundingFraction() {
     assertFormatDescending(
             u"Integer",
-            u"round-integer",
-            NumberFormatter::with().rounding(Rounder::integer()),
+            u"precision-integer",
+            NumberFormatter::with().precision(Precision::integer()),
             Locale::getEnglish(),
             u"87,650",
             u"8,765",
@@ -808,7 +808,7 @@ void NumberFormatterApiTest::roundingFraction() {
     assertFormatDescending(
             u"Fixed Fraction",
             u".000",
-            NumberFormatter::with().rounding(Rounder::fixedFraction(3)),
+            NumberFormatter::with().precision(Precision::fixedFraction(3)),
             Locale::getEnglish(),
             u"87,650.000",
             u"8,765.000",
@@ -823,7 +823,7 @@ void NumberFormatterApiTest::roundingFraction() {
     assertFormatDescending(
             u"Min Fraction",
             u".0+",
-            NumberFormatter::with().rounding(Rounder::minFraction(1)),
+            NumberFormatter::with().precision(Precision::minFraction(1)),
             Locale::getEnglish(),
             u"87,650.0",
             u"8,765.0",
@@ -838,7 +838,7 @@ void NumberFormatterApiTest::roundingFraction() {
     assertFormatDescending(
             u"Max Fraction",
             u".#",
-            NumberFormatter::with().rounding(Rounder::maxFraction(1)),
+            NumberFormatter::with().precision(Precision::maxFraction(1)),
             Locale::getEnglish(),
             u"87,650",
             u"8,765",
@@ -853,7 +853,7 @@ void NumberFormatterApiTest::roundingFraction() {
     assertFormatDescending(
             u"Min/Max Fraction",
             u".0##",
-            NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 3)),
+            NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
             Locale::getEnglish(),
             u"87,650.0",
             u"8,765.0",
@@ -870,7 +870,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Fixed Significant",
             u"@@@",
-            NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
+            NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
             Locale::getEnglish(),
             -98,
             u"-98.0");
@@ -878,7 +878,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Fixed Significant Rounding",
             u"@@@",
-            NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
+            NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
             Locale::getEnglish(),
             -98.7654321,
             u"-98.8");
@@ -886,7 +886,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Fixed Significant Zero",
             u"@@@",
-            NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
+            NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
             Locale::getEnglish(),
             0,
             u"0.00");
@@ -894,7 +894,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Min Significant",
             u"@@+",
-            NumberFormatter::with().rounding(Rounder::minDigits(2)),
+            NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
             Locale::getEnglish(),
             -9,
             u"-9.0");
@@ -902,7 +902,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Max Significant",
             u"@###",
-            NumberFormatter::with().rounding(Rounder::maxDigits(4)),
+            NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
             Locale::getEnglish(),
             98.7654321,
             u"98.77");
@@ -910,7 +910,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Min/Max Significant",
             u"@@@#",
-            NumberFormatter::with().rounding(Rounder::minMaxDigits(3, 4)),
+            NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
             Locale::getEnglish(),
             9.99999,
             u"10.0");
@@ -918,7 +918,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Fixed Significant on zero with lots of integer width",
             u"@ integer-width/+000",
-            NumberFormatter::with().rounding(Rounder::fixedDigits(1))
+            NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
                     .integerWidth(IntegerWidth::zeroFillTo(3)),
             Locale::getEnglish(),
             0,
@@ -927,7 +927,7 @@ void NumberFormatterApiTest::roundingFigures() {
     assertFormatSingle(
             u"Fixed Significant on zero with zero integer width",
             u"@ integer-width/+",
-            NumberFormatter::with().rounding(Rounder::fixedDigits(1))
+            NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
                     .integerWidth(IntegerWidth::zeroFillTo(0)),
             Locale::getEnglish(),
             0,
@@ -938,7 +938,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatDescending(
             u"Basic Significant", // for comparison
             u"@#",
-            NumberFormatter::with().rounding(Rounder::maxDigits(2)),
+            NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
             Locale::getEnglish(),
             u"88,000",
             u"8,800",
@@ -953,7 +953,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatDescending(
             u"FracSig minMaxFrac minSig",
             u".0#/@@@+",
-            NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 2).withMinDigits(3)),
+            NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
             Locale::getEnglish(),
             u"87,650.0",
             u"8,765.0",
@@ -968,7 +968,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatDescending(
             u"FracSig minMaxFrac maxSig A",
             u".0##/@#",
-            NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 3).withMaxDigits(2)),
+            NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
             Locale::getEnglish(),
             u"88,000.0", // maxSig beats maxFrac
             u"8,800.0", // maxSig beats maxFrac
@@ -983,7 +983,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatDescending(
             u"FracSig minMaxFrac maxSig B",
             u".00/@#",
-            NumberFormatter::with().rounding(Rounder::fixedFraction(2).withMaxDigits(2)),
+            NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
             Locale::getEnglish(),
             u"88,000.00", // maxSig beats maxFrac
             u"8,800.00", // maxSig beats maxFrac
@@ -998,7 +998,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatSingle(
             u"FracSig with trailing zeros A",
             u".00/@@@+",
-            NumberFormatter::with().rounding(Rounder::fixedFraction(2).withMinDigits(3)),
+            NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
             Locale::getEnglish(),
             0.1,
             u"0.10");
@@ -1006,7 +1006,7 @@ void NumberFormatterApiTest::roundingFractionFigures() {
     assertFormatSingle(
             u"FracSig with trailing zeros B",
             u".00/@@@+",
-            NumberFormatter::with().rounding(Rounder::fixedFraction(2).withMinDigits(3)),
+            NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
             Locale::getEnglish(),
             0.0999999,
             u"0.10");
@@ -1015,8 +1015,8 @@ void NumberFormatterApiTest::roundingFractionFigures() {
 void NumberFormatterApiTest::roundingOther() {
     assertFormatDescending(
             u"Rounding None",
-            u"round-unlimited",
-            NumberFormatter::with().rounding(Rounder::unlimited()),
+            u"precision-unlimited",
+            NumberFormatter::with().precision(Precision::unlimited()),
             Locale::getEnglish(),
             u"87,650",
             u"8,765",
@@ -1030,8 +1030,8 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Increment",
-            u"round-increment/0.5",
-            NumberFormatter::with().rounding(Rounder::increment(0.5).withMinFraction(1)),
+            u"precision-increment/0.5",
+            NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
             Locale::getEnglish(),
             u"87,650.0",
             u"8,765.0",
@@ -1045,8 +1045,8 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Increment with Min Fraction",
-            u"round-increment/0.50",
-            NumberFormatter::with().rounding(Rounder::increment(0.5).withMinFraction(2)),
+            u"precision-increment/0.50",
+            NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
             Locale::getEnglish(),
             u"87,650.00",
             u"8,765.00",
@@ -1060,8 +1060,8 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Currency Standard",
-            u"currency/CZK round-currency-standard",
-            NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
+            u"currency/CZK precision-currency-standard",
+            NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
                     .unit(CZK),
             Locale::getEnglish(),
             u"CZK 87,650.00",
@@ -1076,8 +1076,8 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Currency Cash",
-            u"currency/CZK round-currency-cash",
-            NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH))
+            u"currency/CZK precision-currency-cash",
+            NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
                     .unit(CZK),
             Locale::getEnglish(),
             u"CZK 87,650",
@@ -1092,8 +1092,8 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Currency Cash with Nickel Rounding",
-            u"currency/CAD round-currency-cash",
-            NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH))
+            u"currency/CAD precision-currency-cash",
+            NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
                     .unit(CAD),
             Locale::getEnglish(),
             u"CA$87,650.00",
@@ -1108,9 +1108,9 @@ void NumberFormatterApiTest::roundingOther() {
 
     assertFormatDescending(
             u"Currency not in top-level fluent chain",
-            u"round-integer", // calling .withCurrency() applies currency rounding rules immediately
-            NumberFormatter::with().rounding(
-                    Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
+            u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
+            NumberFormatter::with().precision(
+                    Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
             Locale::getEnglish(),
             u"87,650",
             u"8,765",
@@ -1125,8 +1125,8 @@ void NumberFormatterApiTest::roundingOther() {
     // NOTE: Other tests cover the behavior of the other rounding modes.
     assertFormatDescending(
             u"Rounding Mode CEILING",
-            u"round-integer/ceiling",
-            NumberFormatter::with().rounding(Rounder::integer().withMode(UNumberFormatRoundingMode::UNUM_ROUND_CEILING)),
+            u"precision-integer rounding-mode-ceiling",
+            NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
             Locale::getEnglish(),
             u"87,650",
             u"8,765",
@@ -2063,7 +2063,7 @@ void NumberFormatterApiTest::formatTypes() {
     // The number needs to have exactly 40 digits, which is the size of the default buffer.
     // (issue discovered by the address sanitizer in C++)
     static const char* str = "0.009876543210987654321098765432109876543211";
-    actual = formatter.rounding(Rounder::unlimited()).formatDecimal(str, status).toString();
+    actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString();
     assertEquals("Format decNumber to 40 digits", str, actual);
 }
 
@@ -2156,8 +2156,8 @@ void NumberFormatterApiTest::fieldPosition() {
 }
 
 void NumberFormatterApiTest::errors() {
-    LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).rounding(
-            Rounder::fixedFraction(
+    LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
+            Precision::fixedFraction(
                     -1));
 
     // formatInt
@@ -2278,16 +2278,16 @@ void NumberFormatterApiTest::validRanges() {
     } \
 }
 
-    VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction, 0);
-    VALID_RANGE_ONEARG(rounding, Rounder::minFraction, 0);
-    VALID_RANGE_ONEARG(rounding, Rounder::maxFraction, 0);
-    VALID_RANGE_TWOARGS(rounding, Rounder::minMaxFraction, 0);
-    VALID_RANGE_ONEARG(rounding, Rounder::fixedDigits, 1);
-    VALID_RANGE_ONEARG(rounding, Rounder::minDigits, 1);
-    VALID_RANGE_ONEARG(rounding, Rounder::maxDigits, 1);
-    VALID_RANGE_TWOARGS(rounding, Rounder::minMaxDigits, 1);
-    VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction(1).withMinDigits, 1);
-    VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction(1).withMaxDigits, 1);
+    VALID_RANGE_ONEARG(rounding, Precision::fixedFraction, 0);
+    VALID_RANGE_ONEARG(rounding, Precision::minFraction, 0);
+    VALID_RANGE_ONEARG(rounding, Precision::maxFraction, 0);
+    VALID_RANGE_TWOARGS(rounding, Precision::minMaxFraction, 0);
+    VALID_RANGE_ONEARG(rounding, Precision::fixedSignificantDigits, 1);
+    VALID_RANGE_ONEARG(rounding, Precision::minSignificantDigits, 1);
+    VALID_RANGE_ONEARG(rounding, Precision::maxSignificantDigits, 1);
+    VALID_RANGE_TWOARGS(rounding, Precision::minMaxSignificantDigits, 1);
+    VALID_RANGE_ONEARG(rounding, Precision::fixedFraction(1).withMinDigits, 1);
+    VALID_RANGE_ONEARG(rounding, Precision::fixedFraction(1).withMaxDigits, 1);
     VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
diff --git a/icu4c/source/test/intltest/numbertest_skeletons.cpp b/icu4c/source/test/intltest/numbertest_skeletons.cpp
index f5ea2a9d076..fa71f34a669 100644
--- a/icu4c/source/test/intltest/numbertest_skeletons.cpp
+++ b/icu4c/source/test/intltest/numbertest_skeletons.cpp
@@ -34,8 +34,8 @@ void NumberSkeletonTest::validTokens() {
     // This tests only if the tokens are valid, not their behavior.
     // Most of these are from the design doc.
     static const char16_t* cases[] = {
-            u"round-integer",
-            u"round-unlimited",
+            u"precision-integer",
+            u"precision-unlimited",
             u"@@@##",
             u"@@+",
             u".000##",
@@ -45,11 +45,11 @@ void NumberSkeletonTest::validTokens() {
             u".######",
             u".00/@@+",
             u".00/@##",
-            u"round-increment/3.14",
-            u"round-currency-standard",
-            u"round-integer/half-up",
-            u".00#/ceiling",
-            u".00/@@+/floor",
+            u"precision-increment/3.14",
+            u"precision-currency-standard",
+            u"precision-integer rounding-mode-half-up",
+            u".00# rounding-mode-ceiling",
+            u".00/@@+ rounding-mode-floor",
             u"scientific",
             u"scientific/+ee",
             u"scientific/sign-always",
@@ -99,9 +99,9 @@ void NumberSkeletonTest::validTokens() {
             u"latin",
             u"numbering-system/arab",
             u"numbering-system/latn",
-            u"round-integer/@##",
-            u"round-integer/ceiling",
-            u"round-currency-cash/ceiling"};
+            u"precision-integer/@##",
+            u"precision-integer rounding-mode-ceiling",
+            u"precision-currency-cash rounding-mode-ceiling"};
 
     for (auto& cas : cases) {
         UnicodeString skeletonString(cas);
@@ -127,12 +127,11 @@ void NumberSkeletonTest::invalidTokens() {
             u".00/@@#",
             u".00/@@#+",
             u".00/floor/@@+", // wrong order
-            u"round-increment/français", // non-invariant characters for C++
-            u"round-currency-cash/XXX",
+            u"precision-increment/français", // non-invariant characters for C++
             u"scientific/ee",
-            u"round-increment/xxx",
-            u"round-increment/NaN",
-            u"round-increment/0.1.2",
+            u"precision-increment/xxx",
+            u"precision-increment/NaN",
+            u"precision-increment/0.1.2",
             u"scale/xxx",
             u"scale/NaN",
             u"scale/0.1.2",
@@ -164,20 +163,20 @@ void NumberSkeletonTest::unknownTokens() {
 void NumberSkeletonTest::unexpectedTokens() {
     static const char16_t* cases[] = {
             u"group-thousands/foo",
-            u"round-integer//ceiling group-off",
-            u"round-integer//ceiling  group-off",
-            u"round-integer/ group-off",
-            u"round-integer// group-off"};
+            u"precision-integer//@## group-off",
+            u"precision-integer//@##  group-off",
+            u"precision-integer/ group-off",
+            u"precision-integer// group-off"};
 
     expectedErrorSkeleton(cases, sizeof(cases) / sizeof(*cases));
 }
 
 void NumberSkeletonTest::duplicateValues() {
     static const char16_t* cases[] = {
-            u"round-integer round-integer",
-            u"round-integer .00+",
-            u"round-integer round-unlimited",
-            u"round-integer @@@",
+            u"precision-integer precision-integer",
+            u"precision-integer .00+",
+            u"precision-integer precision-unlimited",
+            u"precision-integer @@@",
             u"scientific engineering",
             u"engineering compact-long",
             u"sign-auto sign-always"};
@@ -187,14 +186,14 @@ void NumberSkeletonTest::duplicateValues() {
 
 void NumberSkeletonTest::stemsRequiringOption() {
     static const char16_t* stems[] = {
-            u"round-increment",
+            u"precision-increment",
             u"measure-unit",
             u"per-unit",
             u"currency",
             u"integer-width",
             u"numbering-system",
             u"scale"};
-    static const char16_t* suffixes[] = {u"", u"/ceiling", u" scientific", u"/ceiling scientific"};
+    static const char16_t* suffixes[] = {u"", u"/@##", u" scientific", u"/@## scientific"};
 
     for (auto& stem : stems) {
         for (auto& suffix : suffixes) {
@@ -234,10 +233,10 @@ void NumberSkeletonTest::flexibleSeparators() {
     static struct TestCase {
         const char16_t* skeleton;
         const char16_t* expected;
-    } cases[] = {{u"round-integer group-off", u"5142"},
-                 {u"round-integer  group-off", u"5142"},
-                 {u"round-integer/ceiling group-off", u"5143"},
-                 {u"round-integer/ceiling  group-off", u"5143"}};
+    } cases[] = {{u"precision-integer group-off", u"5142"},
+                 {u"precision-integer  group-off", u"5142"},
+                 {u"precision-integer/@## group-off", u"5140"},
+                 {u"precision-integer/@##  group-off", u"5140"}};
 
     for (auto& cas : cases) {
         UnicodeString skeletonString(cas.skeleton);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
index 63a11f48932..bb86ba1ef5b 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
@@ -277,7 +277,7 @@ public class LongNameHandler implements MicroPropsGenerator {
         MicroProps micros = parent.processQuantity(quantity);
         // TODO: Avoid the copy here?
         DecimalQuantity copy = quantity.createCopy();
-        micros.rounding.apply(copy);
+        micros.rounder.apply(copy);
         micros.modOuter = modifiers.get(copy.getStandardPlural(rules));
         return micros;
     }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MacroProps.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MacroProps.java
index 9cb89879e97..84f8fd86972 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MacroProps.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MacroProps.java
@@ -2,14 +2,16 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.impl.number;
 
+import java.math.RoundingMode;
+
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.number.IntegerWidth;
-import com.ibm.icu.number.Scale;
 import com.ibm.icu.number.Notation;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
 import com.ibm.icu.number.NumberFormatter.UnitWidth;
-import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.Precision;
+import com.ibm.icu.number.Scale;
 import com.ibm.icu.text.PluralRules;
 import com.ibm.icu.util.MeasureUnit;
 import com.ibm.icu.util.ULocale;
@@ -18,7 +20,8 @@ public class MacroProps implements Cloneable {
     public Notation notation;
     public MeasureUnit unit;
     public MeasureUnit perUnit;
-    public Rounder rounder;
+    public Precision precision;
+    public RoundingMode roundingMode;
     public Object grouping;
     public Padder padder;
     public IntegerWidth integerWidth;
@@ -45,8 +48,10 @@ public class MacroProps implements Cloneable {
             unit = fallback.unit;
         if (perUnit == null)
             perUnit = fallback.perUnit;
-        if (rounder == null)
-            rounder = fallback.rounder;
+        if (precision == null)
+            precision = fallback.precision;
+        if (roundingMode == null)
+            roundingMode = fallback.roundingMode;
         if (grouping == null)
             grouping = fallback.grouping;
         if (padder == null)
@@ -76,7 +81,8 @@ public class MacroProps implements Cloneable {
         return Utility.hash(notation,
                 unit,
                 perUnit,
-                rounder,
+                precision,
+                roundingMode,
                 grouping,
                 padder,
                 integerWidth,
@@ -102,7 +108,8 @@ public class MacroProps implements Cloneable {
         return Utility.equals(notation, other.notation)
                 && Utility.equals(unit, other.unit)
                 && Utility.equals(perUnit, other.perUnit)
-                && Utility.equals(rounder, other.rounder)
+                && Utility.equals(precision, other.precision)
+                && Utility.equals(roundingMode, other.roundingMode)
                 && Utility.equals(grouping, other.grouping)
                 && Utility.equals(padder, other.padder)
                 && Utility.equals(integerWidth, other.integerWidth)
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
index d5e3ba44f36..6aeda32775e 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
@@ -5,7 +5,7 @@ package com.ibm.icu.impl.number;
 import com.ibm.icu.number.IntegerWidth;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
-import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.Precision;
 import com.ibm.icu.text.DecimalFormatSymbols;
 
 public class MicroProps implements Cloneable, MicroPropsGenerator {
@@ -20,7 +20,7 @@ public class MicroProps implements Cloneable, MicroPropsGenerator {
     public Modifier modOuter;
     public Modifier modMiddle;
     public Modifier modInner;
-    public Rounder rounding;
+    public Precision rounder;
     public Grouper grouping;
     public boolean useCurrency;
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
index 801af947f0e..13d0ab2de10 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
@@ -269,7 +269,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
         if (needsPlurals()) {
             // TODO: Fix this. Avoid the copy.
             DecimalQuantity copy = fq.createCopy();
-            micros.rounding.apply(copy);
+            micros.rounder.apply(copy);
             setNumberProperties(fq.signum(), copy.getStandardPlural(rules));
         } else {
             setNumberProperties(fq.signum(), null);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
index 5219f0091dc..31d1a8404fc 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
@@ -119,16 +119,16 @@ public class CompactNotation extends Notation {
         @Override
         public MicroProps processQuantity(DecimalQuantity quantity) {
             MicroProps micros = parent.processQuantity(quantity);
-            assert micros.rounding != null;
+            assert micros.rounder != null;
 
             // Treat zero as if it had magnitude 0
             int magnitude;
             if (quantity.isZero()) {
                 magnitude = 0;
-                micros.rounding.apply(quantity);
+                micros.rounder.apply(quantity);
             } else {
                 // TODO: Revisit chooseMultiplierAndApply
-                int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data);
+                int multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data);
                 magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
                 magnitude -= multiplier;
             }
@@ -152,7 +152,7 @@ public class CompactNotation extends Notation {
             }
 
             // We already performed rounding. Do not perform it again.
-            micros.rounding = Rounder.constructPassThrough();
+            micros.rounder = Precision.constructPassThrough();
 
             return micros;
         }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyPrecision.java b/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyPrecision.java
new file mode 100644
index 00000000000..09f3f5d1a45
--- /dev/null
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyPrecision.java
@@ -0,0 +1,49 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import com.ibm.icu.util.Currency;
+
+/**
+ * A class that defines a rounding strategy parameterized by a currency to be used when formatting
+ * numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a CurrencyPrecision, use one of the factory methods on Precision.
+ *
+ * @draft ICU 60
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberFormatter
+ */
+public abstract class CurrencyPrecision extends Precision {
+
+    /* package-private */ CurrencyPrecision() {
+    }
+
+    /**
+     * Associates a currency with this rounding strategy.
+     *
+     * <p>
+     * <strong>Calling this method is <em>not required</em></strong>, because the currency specified in
+     * unit() or via a CurrencyAmount passed into format(Measure) is automatically applied to currency
+     * rounding strategies. However, this method enables you to override that automatic association.
+     *
+     * <p>
+     * This method also enables numbers to be formatted using currency rounding rules without explicitly
+     * using a currency format.
+     *
+     * @param currency
+     *            The currency to associate with this rounding strategy.
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public Precision withCurrency(Currency currency) {
+        if (currency != null) {
+            return constructFromCurrency(this, currency);
+        } else {
+            throw new IllegalArgumentException("Currency must not be null");
+        }
+    };
+}
\ No newline at end of file
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyRounder.java
index dd0d0f02ab3..44740512667 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyRounder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/CurrencyRounder.java
@@ -2,48 +2,10 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
-import com.ibm.icu.util.Currency;
-
 /**
- * A class that defines a rounding strategy parameterized by a currency to be used when formatting
- * numbers in NumberFormatter.
- *
- * <p>
- * To create a CurrencyRounder, use one of the factory methods on Rounder.
- *
- * @draft ICU 60
- * @provisional This API might change or be removed in a future release.
- * @see NumberFormatter
+ * @deprecated ICU 62 Use {@link CurrencyPrecision} instead. This class is for backwards compatibility
+ *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
  */
-public abstract class CurrencyRounder extends Rounder {
-
-    /* package-private */ CurrencyRounder() {
-    }
-
-    /**
-     * Associates a currency with this rounding strategy.
-     *
-     * <p>
-     * <strong>Calling this method is <em>not required</em></strong>, because the currency specified in
-     * unit() or via a CurrencyAmount passed into format(Measure) is automatically applied to currency
-     * rounding strategies. However, this method enables you to override that automatic association.
-     *
-     * <p>
-     * This method also enables numbers to be formatted using currency rounding rules without explicitly
-     * using a currency format.
-     *
-     * @param currency
-     *            The currency to associate with this rounding strategy.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public Rounder withCurrency(Currency currency) {
-        if (currency != null) {
-            return constructFromCurrency(this, currency);
-        } else {
-            throw new IllegalArgumentException("Currency must not be null");
-        }
-    };
+@Deprecated
+public abstract class CurrencyRounder extends CurrencyPrecision {
 }
\ No newline at end of file
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/FractionPrecision.java b/icu4j/main/classes/core/src/com/ibm/icu/number/FractionPrecision.java
new file mode 100644
index 00000000000..882a6c51550
--- /dev/null
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/FractionPrecision.java
@@ -0,0 +1,80 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import com.ibm.icu.impl.number.RoundingUtils;
+
+/**
+ * A class that defines a rounding strategy based on a number of fraction places and optionally
+ * significant digits to be used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a FractionPrecision, use one of the factory methods on Precision.
+ *
+ * @draft ICU 60
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberFormatter
+ */
+public abstract class FractionPrecision extends Precision {
+
+    /* package-private */ FractionPrecision() {
+    }
+
+    /**
+     * Ensure that no less than this number of significant digits are retained when rounding according to
+     * fraction rules.
+     *
+     * <p>
+     * For example, with integer rounding, the number 3.141 becomes "3". However, with minimum figures
+     * set to 2, 3.141 becomes "3.1" instead.
+     *
+     * <p>
+     * This setting does not affect the number of trailing zeros. For example, 3.01 would print as "3",
+     * not "3.0".
+     *
+     * @param minSignificantDigits
+     *            The number of significant figures to guarantee.
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public Precision withMinDigits(int minSignificantDigits) {
+        if (minSignificantDigits >= 1 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructFractionSignificant(this, minSignificantDigits, -1);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Ensure that no more than this number of significant digits are retained when rounding according to
+     * fraction rules.
+     *
+     * <p>
+     * For example, with integer rounding, the number 123.4 becomes "123". However, with maximum figures
+     * set to 2, 123.4 becomes "120" instead.
+     *
+     * <p>
+     * This setting does not affect the number of trailing zeros. For example, with fixed fraction of 2,
+     * 123.4 would become "120.00".
+     *
+     * @param maxSignificantDigits
+     *            Round the number to no more than this number of significant figures.
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public Precision withMaxDigits(int maxSignificantDigits) {
+        if (maxSignificantDigits >= 1 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructFractionSignificant(this, -1, maxSignificantDigits);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+}
\ No newline at end of file
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/FractionRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/number/FractionRounder.java
index 61ed736cb7e..56e91503070 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/FractionRounder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/FractionRounder.java
@@ -2,79 +2,10 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
-import com.ibm.icu.impl.number.RoundingUtils;
-
 /**
- * A class that defines a rounding strategy based on a number of fraction places and optionally
- * significant digits to be used when formatting numbers in NumberFormatter.
- *
- * <p>
- * To create a FractionRounder, use one of the factory methods on Rounder.
- *
- * @draft ICU 60
- * @provisional This API might change or be removed in a future release.
- * @see NumberFormatter
+ * @deprecated ICU 62 Use {@link FractionPrecision} instead. This class is for backwards compatibility
+ *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
  */
-public abstract class FractionRounder extends Rounder {
-
-    /* package-private */ FractionRounder() {
-    }
-
-    /**
-     * Ensure that no less than this number of significant digits are retained when rounding according to
-     * fraction rules.
-     *
-     * <p>
-     * For example, with integer rounding, the number 3.141 becomes "3". However, with minimum figures
-     * set to 2, 3.141 becomes "3.1" instead.
-     *
-     * <p>
-     * This setting does not affect the number of trailing zeros. For example, 3.01 would print as "3",
-     * not "3.0".
-     *
-     * @param minSignificantDigits
-     *            The number of significant figures to guarantee.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public Rounder withMinDigits(int minSignificantDigits) {
-        if (minSignificantDigits >= 1 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructFractionSignificant(this, minSignificantDigits, -1);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Ensure that no more than this number of significant digits are retained when rounding according to
-     * fraction rules.
-     *
-     * <p>
-     * For example, with integer rounding, the number 123.4 becomes "123". However, with maximum figures
-     * set to 2, 123.4 becomes "120" instead.
-     *
-     * <p>
-     * This setting does not affect the number of trailing zeros. For example, with fixed fraction of 2,
-     * 123.4 would become "120.00".
-     *
-     * @param maxSignificantDigits
-     *            Round the number to no more than this number of significant figures.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public Rounder withMaxDigits(int maxSignificantDigits) {
-        if (maxSignificantDigits >= 1 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructFractionSignificant(this, -1, maxSignificantDigits);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
+@Deprecated
+public abstract class FractionRounder extends FractionPrecision {
 }
\ No newline at end of file
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
index 881232918c0..b0ce5d12178 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
@@ -20,7 +20,7 @@ import com.ibm.icu.util.ULocale;
  * NumberFormatter.with()
  *     .notation(Notation.compactShort())
  *     .unit(Currency.getInstance("EUR"))
- *     .rounding(Rounder.maxDigits(2))
+ *     .precision(Precision.maxDigits(2))
  *     .locale(...)
  *     .format(1234)
  *     .toString();  // €1.2K in en-US
@@ -28,7 +28,7 @@ import com.ibm.icu.util.ULocale;
  * // Create a formatter in a private static final field:
  * private static final LocalizedNumberFormatter formatter = NumberFormatter.withLocale(...)
  *     .unit(NoUnit.PERCENT)
- *     .rounding(Rounder.fixedFraction(3));
+ *     .precision(Precision.fixedFraction(3));
  * formatter.format(5.9831).toString();  // 5.983% in en-US
  *
  * // Create a "template" in a private static final field but without setting a locale until the call site:
@@ -50,7 +50,7 @@ import com.ibm.icu.util.ULocale;
  * <pre>
  * UnlocalizedNumberFormatter formatter = UnlocalizedNumberFormatter.with()
  *         .notation(Notation.scientific());
- * formatter.rounding(Rounder.maxFraction(2)); // does nothing!
+ * formatter.precision(Precision.maxFraction(2)); // does nothing!
  * formatter.locale(ULocale.ENGLISH).format(9.8765).toString(); // prints "9.8765E0", not "9.88E0"
  * </pre>
  *
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
index 8424e4a0eed..06dad539f33 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
@@ -220,16 +220,19 @@ class NumberFormatterImpl {
         }
 
         // Rounding strategy
-        if (macros.rounder != null) {
-            micros.rounding = macros.rounder;
+        if (macros.precision != null) {
+            micros.rounder = macros.precision;
         } else if (macros.notation instanceof CompactNotation) {
-            micros.rounding = Rounder.COMPACT_STRATEGY;
+            micros.rounder = Precision.COMPACT_STRATEGY;
         } else if (isCurrency) {
-            micros.rounding = Rounder.MONETARY_STANDARD;
+            micros.rounder = Precision.MONETARY_STANDARD;
         } else {
-            micros.rounding = Rounder.DEFAULT_MAX_FRAC_6;
+            micros.rounder = Precision.DEFAULT_MAX_FRAC_6;
         }
-        micros.rounding = micros.rounding.withLocaleData(currency);
+        if (macros.roundingMode != null) {
+            micros.rounder = micros.rounder.withMode(macros.roundingMode);
+        }
+        micros.rounder = micros.rounder.withLocaleData(currency);
 
         // Grouping strategy
         if (macros.grouping instanceof Grouper) {
@@ -360,7 +363,7 @@ class NumberFormatterImpl {
             MicroProps micros,
             DecimalQuantity quantity,
             NumberStringBuilder string) {
-        micros.rounding.apply(quantity);
+        micros.rounder.apply(quantity);
         if (micros.integerWidth.maxInt == -1) {
             quantity.setIntegerLength(micros.integerWidth.minInt, Integer.MAX_VALUE);
         } else {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterSettings.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterSettings.java
index 70cf69389c7..9633a44e730 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterSettings.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterSettings.java
@@ -2,6 +2,8 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
+import java.math.RoundingMode;
+
 import com.ibm.icu.impl.number.MacroProps;
 import com.ibm.icu.impl.number.Padder;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
@@ -31,18 +33,19 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
     static final int KEY_LOCALE = 1;
     static final int KEY_NOTATION = 2;
     static final int KEY_UNIT = 3;
-    static final int KEY_ROUNDER = 4;
-    static final int KEY_GROUPING = 5;
-    static final int KEY_PADDER = 6;
-    static final int KEY_INTEGER = 7;
-    static final int KEY_SYMBOLS = 8;
-    static final int KEY_UNIT_WIDTH = 9;
-    static final int KEY_SIGN = 10;
-    static final int KEY_DECIMAL = 11;
-    static final int KEY_SCALE = 12;
-    static final int KEY_THRESHOLD = 13;
-    static final int KEY_PER_UNIT = 14;
-    static final int KEY_MAX = 15;
+    static final int KEY_PRECISION = 4;
+    static final int KEY_ROUNDING_MODE = 5;
+    static final int KEY_GROUPING = 6;
+    static final int KEY_PADDER = 7;
+    static final int KEY_INTEGER = 8;
+    static final int KEY_SYMBOLS = 9;
+    static final int KEY_UNIT_WIDTH = 10;
+    static final int KEY_SIGN = 11;
+    static final int KEY_DECIMAL = 12;
+    static final int KEY_SCALE = 13;
+    static final int KEY_THRESHOLD = 14;
+    static final int KEY_PER_UNIT = 15;
+    static final int KEY_MAX = 16;
 
     final NumberFormatterSettings<?> parent;
     final int key;
@@ -175,7 +178,7 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
     }
 
     /**
-     * Specifies the rounding strategy to use when formatting numbers.
+     * Specifies the rounding precision to use when formatting numbers.
      *
      * <ul>
      * <li>Round to 3 decimal places: "3.142"
@@ -185,28 +188,62 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
      * </ul>
      *
      * <p>
-     * Pass this method the return value of one of the factory methods on {@link Rounder}. For example:
+     * Pass this method the return value of one of the factory methods on {@link Precision}. For example:
      *
      * <pre>
-     * NumberFormatter.with().rounding(Rounder.fixedFraction(2))
+     * NumberFormatter.with().precision(Precision.fixedFraction(2))
      * </pre>
      *
      * <p>
-     * In most cases, the default rounding strategy is to round to 6 fraction places; i.e.,
-     * <code>Rounder.maxFraction(6)</code>. The exceptions are if compact notation is being used, then
-     * the compact notation rounding strategy is used (see {@link Notation#compactShort} for details), or
+     * In most cases, the default rounding precision is to round to 6 fraction places; i.e.,
+     * <code>Precision.maxFraction(6)</code>. The exceptions are if compact notation is being used, then
+     * the compact notation rounding precision is used (see {@link Notation#compactShort} for details), or
      * if the unit is a currency, then standard currency rounding is used, which varies from currency to
-     * currency (see {@link Rounder#currency} for details).
+     * currency (see {@link Precision#currency} for details).
      *
-     * @param rounder
-     *            The rounding strategy to use.
+     * @param precision
+     *            The rounding precision to use.
      * @return The fluent chain.
-     * @see Rounder
+     * @see Precision
      * @draft ICU 60
      * @provisional This API might change or be removed in a future release.
      */
-    public T rounding(Rounder rounder) {
-        return create(KEY_ROUNDER, rounder);
+    public T precision(Precision precision) {
+        return create(KEY_PRECISION, precision);
+    }
+
+    /**
+     * @deprecated ICU 62 Use precision() instead. This method is for backwards compatibility and will be
+     *             removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
+     */
+    @Deprecated
+    public T rounding(Precision rounder) {
+        return precision(rounder);
+    }
+
+    /**
+     * Specifies how to determine the direction to round a number when it has more digits than fit in the
+     * desired precision.  When formatting 1.235:
+     *
+     * <ul>
+     * <li>Ceiling rounding mode with integer precision: "2"
+     * <li>Half-down rounding mode with 2 fixed fraction digits: "1.23"
+     * <li>Half-up rounding mode with 2 fixed fraction digits: "1.24"
+     * </ul>
+     *
+     * The default is HALF_EVEN. For more information on rounding mode, see the ICU userguide here:
+     *
+     * http://userguide.icu-project.org/formatparse/numbers/rounding-modes
+     *
+     * @param roundingMode
+     *            The rounding mode to use.
+     * @return The fluent chain.
+     * @see Precision
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     */
+    public T roundingMode(RoundingMode roundingMode) {
+        return create(KEY_ROUNDING_MODE, roundingMode);
     }
 
     /**
@@ -559,9 +596,14 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
                     macros.unit = (MeasureUnit) current.value;
                 }
                 break;
-            case KEY_ROUNDER:
-                if (macros.rounder == null) {
-                    macros.rounder = (Rounder) current.value;
+            case KEY_PRECISION:
+                if (macros.precision == null) {
+                    macros.precision = (Precision) current.value;
+                }
+                break;
+            case KEY_ROUNDING_MODE:
+                if (macros.roundingMode == null) {
+                    macros.roundingMode = (RoundingMode) current.value;
                 }
                 break;
             case KEY_GROUPING:
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
index 99af4cd5224..aa0cca35379 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
@@ -17,9 +17,9 @@ import com.ibm.icu.impl.number.PropertiesAffixPatternProvider;
 import com.ibm.icu.impl.number.RoundingUtils;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
-import com.ibm.icu.number.Rounder.FractionRounderImpl;
-import com.ibm.icu.number.Rounder.IncrementRounderImpl;
-import com.ibm.icu.number.Rounder.SignificantRounderImpl;
+import com.ibm.icu.number.Precision.FractionRounderImpl;
+import com.ibm.icu.number.Precision.IncrementRounderImpl;
+import com.ibm.icu.number.Precision.SignificantRounderImpl;
 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
 import com.ibm.icu.text.DecimalFormatSymbols;
 import com.ibm.icu.util.Currency;
@@ -170,11 +170,11 @@ final class NumberPropertyMapper {
             maxInt = maxInt < 0 ? -1
                     : maxInt < minInt ? minInt : maxInt > RoundingUtils.MAX_INT_FRAC_SIG ? -1 : maxInt;
         }
-        Rounder rounding = null;
+        Precision rounding = null;
         if (explicitCurrencyUsage) {
-            rounding = Rounder.constructCurrency(currencyUsage).withCurrency(currency);
+            rounding = Precision.constructCurrency(currencyUsage).withCurrency(currency);
         } else if (roundingIncrement != null) {
-            rounding = Rounder.constructIncrement(roundingIncrement);
+            rounding = Precision.constructIncrement(roundingIncrement);
         } else if (explicitMinMaxSig) {
             minSig = minSig < 1 ? 1
                     : minSig > RoundingUtils.MAX_INT_FRAC_SIG ? RoundingUtils.MAX_INT_FRAC_SIG : minSig;
@@ -182,15 +182,15 @@ final class NumberPropertyMapper {
                     : maxSig < minSig ? minSig
                             : maxSig > RoundingUtils.MAX_INT_FRAC_SIG ? RoundingUtils.MAX_INT_FRAC_SIG
                                     : maxSig;
-            rounding = Rounder.constructSignificant(minSig, maxSig);
+            rounding = Precision.constructSignificant(minSig, maxSig);
         } else if (explicitMinMaxFrac) {
-            rounding = Rounder.constructFraction(minFrac, maxFrac);
+            rounding = Precision.constructFraction(minFrac, maxFrac);
         } else if (useCurrency) {
-            rounding = Rounder.constructCurrency(currencyUsage);
+            rounding = Precision.constructCurrency(currencyUsage);
         }
         if (rounding != null) {
             rounding = rounding.withMode(mathContext);
-            macros.rounder = rounding;
+            macros.precision = rounding;
         }
 
         ///////////////////
@@ -256,7 +256,7 @@ final class NumberPropertyMapper {
                     properties.getExponentSignAlwaysShown() ? SignDisplay.ALWAYS : SignDisplay.AUTO);
             // Scientific notation also involves overriding the rounding mode.
             // TODO: Overriding here is a bit of a hack. Should this logic go earlier?
-            if (macros.rounder instanceof FractionRounder) {
+            if (macros.precision instanceof FractionPrecision) {
                 // For the purposes of rounding, get the original min/max int/frac, since the local
                 // variables
                 // have been manipulated for display purposes.
@@ -265,13 +265,13 @@ final class NumberPropertyMapper {
                 int maxFrac_ = properties.getMaximumFractionDigits();
                 if (minInt_ == 0 && maxFrac_ == 0) {
                     // Patterns like "#E0" and "##E0", which mean no rounding!
-                    macros.rounder = Rounder.constructInfinite().withMode(mathContext);
+                    macros.precision = Precision.constructInfinite().withMode(mathContext);
                 } else if (minInt_ == 0 && minFrac_ == 0) {
                     // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
-                    macros.rounder = Rounder.constructSignificant(1, maxFrac_ + 1).withMode(mathContext);
+                    macros.precision = Precision.constructSignificant(1, maxFrac_ + 1).withMode(mathContext);
                 } else {
                     // All other scientific patterns, which mean round to minInt+maxFrac
-                    macros.rounder = Rounder.constructSignificant(minInt_ + minFrac_, minInt_ + maxFrac_)
+                    macros.precision = Precision.constructSignificant(minInt_ + minFrac_, minInt_ + maxFrac_)
                             .withMode(mathContext);
                 }
             }
@@ -311,9 +311,9 @@ final class NumberPropertyMapper {
             exportedProperties.setMinimumIntegerDigits(minInt);
             exportedProperties.setMaximumIntegerDigits(maxInt == -1 ? Integer.MAX_VALUE : maxInt);
 
-            Rounder rounding_;
-            if (rounding instanceof CurrencyRounder) {
-                rounding_ = ((CurrencyRounder) rounding).withCurrency(currency);
+            Precision rounding_;
+            if (rounding instanceof CurrencyPrecision) {
+                rounding_ = ((CurrencyPrecision) rounding).withCurrency(currency);
             } else {
                 rounding_ = rounding;
             }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
index 707ea0cedba..e93fd0db27e 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
@@ -47,11 +47,10 @@ class NumberSkeletonImpl {
 
         // Section 1: We might accept an option, but it is not required:
         STATE_SCIENTIFIC,
-        STATE_ROUNDER,
-        STATE_FRACTION_ROUNDER,
+        STATE_FRACTION_PRECISION,
 
         // Section 2: An option is required:
-        STATE_INCREMENT_ROUNDER,
+        STATE_INCREMENT_PRECISION,
         STATE_MEASURE_UNIT,
         STATE_PER_MEASURE_UNIT,
         STATE_CURRENCY_UNIT,
@@ -77,10 +76,18 @@ class NumberSkeletonImpl {
         STEM_BASE_UNIT,
         STEM_PERCENT,
         STEM_PERMILLE,
-        STEM_ROUND_INTEGER,
-        STEM_ROUND_UNLIMITED,
-        STEM_ROUND_CURRENCY_STANDARD,
-        STEM_ROUND_CURRENCY_CASH,
+        STEM_PRECISION_INTEGER,
+        STEM_PRECISION_UNLIMITED,
+        STEM_PRECISION_CURRENCY_STANDARD,
+        STEM_PRECISION_CURRENCY_CASH,
+        STEM_ROUNDING_MODE_CEILING,
+        STEM_ROUNDING_MODE_FLOOR,
+        STEM_ROUNDING_MODE_DOWN,
+        STEM_ROUNDING_MODE_UP,
+        STEM_ROUNDING_MODE_HALF_EVEN,
+        STEM_ROUNDING_MODE_HALF_DOWN,
+        STEM_ROUNDING_MODE_HALF_UP,
+        STEM_ROUNDING_MODE_UNNECESSARY,
         STEM_GROUP_OFF,
         STEM_GROUP_MIN2,
         STEM_GROUP_AUTO,
@@ -103,7 +110,7 @@ class NumberSkeletonImpl {
         STEM_DECIMAL_ALWAYS,
 
         // Section 2: Stems that DO require an option:
-        STEM_ROUND_INCREMENT,
+        STEM_PRECISION_INCREMENT,
         STEM_MEASURE_UNIT,
         STEM_PER_MEASURE_UNIT,
         STEM_CURRENCY,
@@ -130,10 +137,18 @@ class NumberSkeletonImpl {
         b.add("base-unit", StemEnum.STEM_BASE_UNIT.ordinal());
         b.add("percent", StemEnum.STEM_PERCENT.ordinal());
         b.add("permille", StemEnum.STEM_PERMILLE.ordinal());
-        b.add("round-integer", StemEnum.STEM_ROUND_INTEGER.ordinal());
-        b.add("round-unlimited", StemEnum.STEM_ROUND_UNLIMITED.ordinal());
-        b.add("round-currency-standard", StemEnum.STEM_ROUND_CURRENCY_STANDARD.ordinal());
-        b.add("round-currency-cash", StemEnum.STEM_ROUND_CURRENCY_CASH.ordinal());
+        b.add("precision-integer", StemEnum.STEM_PRECISION_INTEGER.ordinal());
+        b.add("precision-unlimited", StemEnum.STEM_PRECISION_UNLIMITED.ordinal());
+        b.add("precision-currency-standard", StemEnum.STEM_PRECISION_CURRENCY_STANDARD.ordinal());
+        b.add("precision-currency-cash", StemEnum.STEM_PRECISION_CURRENCY_CASH.ordinal());
+        b.add("rounding-mode-ceiling", StemEnum.STEM_ROUNDING_MODE_CEILING.ordinal());
+        b.add("rounding-mode-floor", StemEnum.STEM_ROUNDING_MODE_FLOOR.ordinal());
+        b.add("rounding-mode-down", StemEnum.STEM_ROUNDING_MODE_DOWN.ordinal());
+        b.add("rounding-mode-up", StemEnum.STEM_ROUNDING_MODE_UP.ordinal());
+        b.add("rounding-mode-half-even", StemEnum.STEM_ROUNDING_MODE_HALF_EVEN.ordinal());
+        b.add("rounding-mode-half-down", StemEnum.STEM_ROUNDING_MODE_HALF_DOWN.ordinal());
+        b.add("rounding-mode-half-up", StemEnum.STEM_ROUNDING_MODE_HALF_UP.ordinal());
+        b.add("rounding-mode-unnecessary", StemEnum.STEM_ROUNDING_MODE_UNNECESSARY.ordinal());
         b.add("group-off", StemEnum.STEM_GROUP_OFF.ordinal());
         b.add("group-min2", StemEnum.STEM_GROUP_MIN2.ordinal());
         b.add("group-auto", StemEnum.STEM_GROUP_AUTO.ordinal());
@@ -156,7 +171,7 @@ class NumberSkeletonImpl {
         b.add("decimal-always", StemEnum.STEM_DECIMAL_ALWAYS.ordinal());
 
         // Section 2:
-        b.add("round-increment", StemEnum.STEM_ROUND_INCREMENT.ordinal());
+        b.add("precision-increment", StemEnum.STEM_PRECISION_INCREMENT.ordinal());
         b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal());
         b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal());
         b.add("currency", StemEnum.STEM_CURRENCY.ordinal());
@@ -205,16 +220,39 @@ class NumberSkeletonImpl {
             }
         }
 
-        private static Rounder rounder(StemEnum stem) {
+        private static Precision precision(StemEnum stem) {
             switch (stem) {
-            case STEM_ROUND_INTEGER:
-                return Rounder.integer();
-            case STEM_ROUND_UNLIMITED:
-                return Rounder.unlimited();
-            case STEM_ROUND_CURRENCY_STANDARD:
-                return Rounder.currency(CurrencyUsage.STANDARD);
-            case STEM_ROUND_CURRENCY_CASH:
-                return Rounder.currency(CurrencyUsage.CASH);
+            case STEM_PRECISION_INTEGER:
+                return Precision.integer();
+            case STEM_PRECISION_UNLIMITED:
+                return Precision.unlimited();
+            case STEM_PRECISION_CURRENCY_STANDARD:
+                return Precision.currency(CurrencyUsage.STANDARD);
+            case STEM_PRECISION_CURRENCY_CASH:
+                return Precision.currency(CurrencyUsage.CASH);
+            default:
+                throw new AssertionError();
+            }
+        }
+
+        private static RoundingMode roundingMode(StemEnum stem) {
+            switch (stem) {
+            case STEM_ROUNDING_MODE_CEILING:
+                return RoundingMode.CEILING;
+            case STEM_ROUNDING_MODE_FLOOR:
+                return RoundingMode.FLOOR;
+            case STEM_ROUNDING_MODE_DOWN:
+                return RoundingMode.DOWN;
+            case STEM_ROUNDING_MODE_UP:
+                return RoundingMode.UP;
+            case STEM_ROUNDING_MODE_HALF_EVEN:
+                return RoundingMode.HALF_EVEN;
+            case STEM_ROUNDING_MODE_HALF_DOWN:
+                return RoundingMode.HALF_DOWN;
+            case STEM_ROUNDING_MODE_HALF_UP:
+                return RoundingMode.HALF_UP;
+            case STEM_ROUNDING_MODE_UNNECESSARY:
+                return RoundingMode.UNNECESSARY;
             default:
                 throw new AssertionError();
             }
@@ -293,6 +331,37 @@ class NumberSkeletonImpl {
      */
     static final class EnumToStemString {
 
+        private static void roundingMode(RoundingMode value, StringBuilder sb) {
+            switch (value) {
+            case CEILING:
+                sb.append("rounding-mode-ceiling");
+                break;
+            case FLOOR:
+                sb.append("rounding-mode-floor");
+                break;
+            case DOWN:
+                sb.append("rounding-mode-down");
+                break;
+            case UP:
+                sb.append("rounding-mode-up");
+                break;
+            case HALF_EVEN:
+                sb.append("rounding-mode-half-even");
+                break;
+            case HALF_DOWN:
+                sb.append("rounding-mode-half-down");
+                break;
+            case HALF_UP:
+                sb.append("rounding-mode-half-up");
+                break;
+            case UNNECESSARY:
+                sb.append("rounding-mode-unnecessary");
+                break;
+            default:
+                throw new AssertionError();
+            }
+        }
+
         private static void groupingStrategy(GroupingStrategy value, StringBuilder sb) {
             switch (value) {
             case OFF:
@@ -379,17 +448,6 @@ class NumberSkeletonImpl {
         }
     }
 
-    /** Kebab case versions of the rounding mode enum. */
-    static final String[] ROUNDING_MODE_STRINGS = {
-            "up",
-            "down",
-            "ceiling",
-            "floor",
-            "half-up",
-            "half-down",
-            "half-even",
-            "unnecessary" };
-
     ///// ENTRYPOINT FUNCTIONS /////
 
     /** Cache for parsed skeleton strings. */
@@ -508,7 +566,7 @@ class NumberSkeletonImpl {
             // Does the current stem require an option?
             if (isTokenSeparator && stem != ParseState.STATE_NULL) {
                 switch (stem) {
-                case STATE_INCREMENT_ROUNDER:
+                case STATE_INCREMENT_PRECISION:
                 case STATE_MEASURE_UNIT:
                 case STATE_PER_MEASURE_UNIT:
                 case STATE_CURRENCY_UNIT:
@@ -539,11 +597,11 @@ class NumberSkeletonImpl {
         // First check for "blueprint" stems, which start with a "signal char"
         switch (segment.charAt(0)) {
         case '.':
-            checkNull(macros.rounder, segment);
+            checkNull(macros.precision, segment);
             BlueprintHelpers.parseFractionStem(segment, macros);
-            return ParseState.STATE_FRACTION_ROUNDER;
+            return ParseState.STATE_FRACTION_PRECISION;
         case '@':
-            checkNull(macros.rounder, segment);
+            checkNull(macros.precision, segment);
             BlueprintHelpers.parseDigitsStem(segment, macros);
             return ParseState.STATE_NULL;
         }
@@ -583,19 +641,31 @@ class NumberSkeletonImpl {
             macros.unit = StemToObject.unit(stem);
             return ParseState.STATE_NULL;
 
-        case STEM_ROUND_INTEGER:
-        case STEM_ROUND_UNLIMITED:
-        case STEM_ROUND_CURRENCY_STANDARD:
-        case STEM_ROUND_CURRENCY_CASH:
-            checkNull(macros.rounder, segment);
-            macros.rounder = StemToObject.rounder(stem);
+        case STEM_PRECISION_INTEGER:
+        case STEM_PRECISION_UNLIMITED:
+        case STEM_PRECISION_CURRENCY_STANDARD:
+        case STEM_PRECISION_CURRENCY_CASH:
+            checkNull(macros.precision, segment);
+            macros.precision = StemToObject.precision(stem);
             switch (stem) {
-            case STEM_ROUND_INTEGER:
-                return ParseState.STATE_FRACTION_ROUNDER; // allows for "round-integer/@##"
+            case STEM_PRECISION_INTEGER:
+                return ParseState.STATE_FRACTION_PRECISION; // allows for "precision-integer/@##"
             default:
-                return ParseState.STATE_ROUNDER; // allows for rounding mode options
+                return ParseState.STATE_NULL;
             }
 
+        case STEM_ROUNDING_MODE_CEILING:
+        case STEM_ROUNDING_MODE_FLOOR:
+        case STEM_ROUNDING_MODE_DOWN:
+        case STEM_ROUNDING_MODE_UP:
+        case STEM_ROUNDING_MODE_HALF_EVEN:
+        case STEM_ROUNDING_MODE_HALF_DOWN:
+        case STEM_ROUNDING_MODE_HALF_UP:
+        case STEM_ROUNDING_MODE_UNNECESSARY:
+            checkNull(macros.roundingMode, segment);
+            macros.roundingMode = StemToObject.roundingMode(stem);
+            return ParseState.STATE_NULL;
+
         case STEM_GROUP_OFF:
         case STEM_GROUP_MIN2:
         case STEM_GROUP_AUTO:
@@ -638,9 +708,9 @@ class NumberSkeletonImpl {
 
         // Stems requiring an option:
 
-        case STEM_ROUND_INCREMENT:
-            checkNull(macros.rounder, segment);
-            return ParseState.STATE_INCREMENT_ROUNDER;
+        case STEM_PRECISION_INCREMENT:
+            checkNull(macros.precision, segment);
+            return ParseState.STATE_INCREMENT_PRECISION;
 
         case STEM_MEASURE_UNIT:
             checkNull(macros.unit, segment);
@@ -691,9 +761,9 @@ class NumberSkeletonImpl {
         case STATE_PER_MEASURE_UNIT:
             BlueprintHelpers.parseMeasurePerUnitOption(segment, macros);
             return ParseState.STATE_NULL;
-        case STATE_INCREMENT_ROUNDER:
+        case STATE_INCREMENT_PRECISION:
             BlueprintHelpers.parseIncrementOption(segment, macros);
-            return ParseState.STATE_ROUNDER;
+            return ParseState.STATE_NULL;
         case STATE_INTEGER_WIDTH:
             BlueprintHelpers.parseIntegerWidthOption(segment, macros);
             return ParseState.STATE_NULL;
@@ -725,21 +795,9 @@ class NumberSkeletonImpl {
 
         // Frac-sig option
         switch (stem) {
-        case STATE_FRACTION_ROUNDER:
+        case STATE_FRACTION_PRECISION:
             if (BlueprintHelpers.parseFracSigOption(segment, macros)) {
-                return ParseState.STATE_ROUNDER;
-            }
-            break;
-        default:
-            break;
-        }
-
-        // Rounding mode option
-        switch (stem) {
-        case STATE_ROUNDER:
-        case STATE_FRACTION_ROUNDER:
-            if (BlueprintHelpers.parseRoundingModeOption(segment, macros)) {
-                return ParseState.STATE_ROUNDER;
+                return ParseState.STATE_NULL;
             }
             break;
         default:
@@ -767,7 +825,10 @@ class NumberSkeletonImpl {
         if (macros.perUnit != null && GeneratorHelpers.perUnit(macros, sb)) {
             sb.append(' ');
         }
-        if (macros.rounder != null && GeneratorHelpers.rounding(macros, sb)) {
+        if (macros.precision != null && GeneratorHelpers.precision(macros, sb)) {
+            sb.append(' ');
+        }
+        if (macros.roundingMode != null && GeneratorHelpers.roundingMode(macros, sb)) {
             sb.append(' ');
         }
         if (macros.grouping != null && GeneratorHelpers.grouping(macros, sb)) {
@@ -951,15 +1012,15 @@ class NumberSkeletonImpl {
             }
             // Use the public APIs to enforce bounds checking
             if (maxFrac == -1) {
-                macros.rounder = Rounder.minFraction(minFrac);
+                macros.precision = Precision.minFraction(minFrac);
             } else {
-                macros.rounder = Rounder.minMaxFraction(minFrac, maxFrac);
+                macros.precision = Precision.minMaxFraction(minFrac, maxFrac);
             }
         }
 
         private static void generateFractionStem(int minFrac, int maxFrac, StringBuilder sb) {
             if (minFrac == 0 && maxFrac == 0) {
-                sb.append("round-integer");
+                sb.append("precision-integer");
                 return;
             }
             sb.append('.');
@@ -1005,9 +1066,9 @@ class NumberSkeletonImpl {
             }
             // Use the public APIs to enforce bounds checking
             if (maxSig == -1) {
-                macros.rounder = Rounder.minDigits(minSig);
+                macros.precision = Precision.minSignificantDigits(minSig);
             } else {
-                macros.rounder = Rounder.minMaxDigits(minSig, maxSig);
+                macros.precision = Precision.minMaxSignificantDigits(minSig, maxSig);
             }
         }
 
@@ -1066,11 +1127,11 @@ class NumberSkeletonImpl {
                 throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
             }
 
-            FractionRounder oldRounder = (FractionRounder) macros.rounder;
+            FractionPrecision oldRounder = (FractionPrecision) macros.precision;
             if (maxSig == -1) {
-                macros.rounder = oldRounder.withMinDigits(minSig);
+                macros.precision = oldRounder.withMinDigits(minSig);
             } else {
-                macros.rounder = oldRounder.withMaxDigits(maxSig);
+                macros.precision = oldRounder.withMaxDigits(maxSig);
             }
             return true;
         }
@@ -1084,28 +1145,13 @@ class NumberSkeletonImpl {
             } catch (NumberFormatException e) {
                 throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
             }
-            macros.rounder = Rounder.increment(increment);
+            macros.precision = Precision.increment(increment);
         }
 
         private static void generateIncrementOption(BigDecimal increment, StringBuilder sb) {
             sb.append(increment.toPlainString());
         }
 
-        /** @return Whether we successfully found and parsed a rounding mode. */
-        private static boolean parseRoundingModeOption(StringSegment segment, MacroProps macros) {
-            for (int rm = 0; rm < ROUNDING_MODE_STRINGS.length; rm++) {
-                if (segment.equals(ROUNDING_MODE_STRINGS[rm])) {
-                    macros.rounder = macros.rounder.withMode(RoundingMode.valueOf(rm));
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private static void generateRoundingModeOption(RoundingMode mode, StringBuilder sb) {
-            sb.append(ROUNDING_MODE_STRINGS[mode.ordinal()]);
-        }
-
         private static void parseIntegerWidthOption(StringSegment segment, MacroProps macros) {
             int offset = 0;
             int minInt = 0;
@@ -1272,17 +1318,17 @@ class NumberSkeletonImpl {
             }
         }
 
-        private static boolean rounding(MacroProps macros, StringBuilder sb) {
-            if (macros.rounder instanceof Rounder.InfiniteRounderImpl) {
-                sb.append("round-unlimited");
-            } else if (macros.rounder instanceof Rounder.FractionRounderImpl) {
-                Rounder.FractionRounderImpl impl = (Rounder.FractionRounderImpl) macros.rounder;
+        private static boolean precision(MacroProps macros, StringBuilder sb) {
+            if (macros.precision instanceof Precision.InfiniteRounderImpl) {
+                sb.append("precision-unlimited");
+            } else if (macros.precision instanceof Precision.FractionRounderImpl) {
+                Precision.FractionRounderImpl impl = (Precision.FractionRounderImpl) macros.precision;
                 BlueprintHelpers.generateFractionStem(impl.minFrac, impl.maxFrac, sb);
-            } else if (macros.rounder instanceof Rounder.SignificantRounderImpl) {
-                Rounder.SignificantRounderImpl impl = (Rounder.SignificantRounderImpl) macros.rounder;
+            } else if (macros.precision instanceof Precision.SignificantRounderImpl) {
+                Precision.SignificantRounderImpl impl = (Precision.SignificantRounderImpl) macros.precision;
                 BlueprintHelpers.generateDigitsStem(impl.minSig, impl.maxSig, sb);
-            } else if (macros.rounder instanceof Rounder.FracSigRounderImpl) {
-                Rounder.FracSigRounderImpl impl = (Rounder.FracSigRounderImpl) macros.rounder;
+            } else if (macros.precision instanceof Precision.FracSigRounderImpl) {
+                Precision.FracSigRounderImpl impl = (Precision.FracSigRounderImpl) macros.precision;
                 BlueprintHelpers.generateFractionStem(impl.minFrac, impl.maxFrac, sb);
                 sb.append('/');
                 if (impl.minSig == -1) {
@@ -1290,31 +1336,32 @@ class NumberSkeletonImpl {
                 } else {
                     BlueprintHelpers.generateDigitsStem(impl.minSig, -1, sb);
                 }
-            } else if (macros.rounder instanceof Rounder.IncrementRounderImpl) {
-                Rounder.IncrementRounderImpl impl = (Rounder.IncrementRounderImpl) macros.rounder;
-                sb.append("round-increment/");
+            } else if (macros.precision instanceof Precision.IncrementRounderImpl) {
+                Precision.IncrementRounderImpl impl = (Precision.IncrementRounderImpl) macros.precision;
+                sb.append("precision-increment/");
                 BlueprintHelpers.generateIncrementOption(impl.increment, sb);
             } else {
-                assert macros.rounder instanceof Rounder.CurrencyRounderImpl;
-                Rounder.CurrencyRounderImpl impl = (Rounder.CurrencyRounderImpl) macros.rounder;
+                assert macros.precision instanceof Precision.CurrencyRounderImpl;
+                Precision.CurrencyRounderImpl impl = (Precision.CurrencyRounderImpl) macros.precision;
                 if (impl.usage == CurrencyUsage.STANDARD) {
-                    sb.append("round-currency-standard");
+                    sb.append("precision-currency-standard");
                 } else {
-                    sb.append("round-currency-cash");
+                    sb.append("precision-currency-cash");
                 }
             }
 
-            // Generate the options
-            if (macros.rounder.mathContext != RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED) {
-                sb.append('/');
-                BlueprintHelpers.generateRoundingModeOption(macros.rounder.mathContext.getRoundingMode(),
-                        sb);
-            }
-
             // NOTE: Always return true for rounding because the default value depends on other options.
             return true;
         }
 
+        private static boolean roundingMode(MacroProps macros, StringBuilder sb) {
+            if (macros.roundingMode == RoundingUtils.DEFAULT_ROUNDING_MODE) {
+                return false; // Default value
+            }
+            EnumToStemString.roundingMode(macros.roundingMode, sb);
+            return true;
+        }
+
         private static boolean grouping(MacroProps macros, StringBuilder sb) {
             if (macros.grouping instanceof GroupingStrategy) {
                 if (macros.grouping == GroupingStrategy.AUTO) {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java b/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
new file mode 100644
index 00000000000..78a90e10a02
--- /dev/null
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
@@ -0,0 +1,759 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+
+import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.MultiplierProducer;
+import com.ibm.icu.impl.number.RoundingUtils;
+import com.ibm.icu.util.Currency;
+import com.ibm.icu.util.Currency.CurrencyUsage;
+
+/**
+ * A class that defines the rounding precision to be used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a Precision, use one of the factory methods.
+ *
+ * @draft ICU 62
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberFormatter
+ */
+public abstract class Precision implements Cloneable {
+
+    /* package-private final */ MathContext mathContext;
+
+    /* package-private */ Precision() {
+        mathContext = RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED;
+    }
+
+    /**
+     * Show all available digits to full precision.
+     *
+     * <p>
+     * <strong>NOTE:</strong> When formatting a <em>double</em>, this method, along with
+     * {@link #minFraction} and {@link #minDigits}, will trigger complex algorithm similar to
+     * <em>Dragon4</em> to determine the low-order digits and the number of digits to display based on
+     * the value of the double. If the number of fraction places or significant digits can be bounded,
+     * consider using {@link #maxFraction} or {@link #maxDigits} instead to maximize performance. For
+     * more information, read the following blog post.
+     *
+     * <p>
+     * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/
+     *
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision unlimited() {
+        return constructInfinite();
+    }
+
+    /**
+     * Show numbers rounded if necessary to the nearest integer.
+     *
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static FractionPrecision integer() {
+        return constructFraction(0, 0);
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
+     * decimal separator). Additionally, pad with zeros to ensure that this number of places are always
+     * shown.
+     *
+     * <p>
+     * Example output with minMaxFractionPlaces = 3:
+     *
+     * <p>
+     * 87,650.000<br>
+     * 8,765.000<br>
+     * 876.500<br>
+     * 87.650<br>
+     * 8.765<br>
+     * 0.876<br>
+     * 0.088<br>
+     * 0.009<br>
+     * 0.000 (zero)
+     *
+     * <p>
+     * This method is equivalent to {@link #minMaxFraction} with both arguments equal.
+     *
+     * @param minMaxFractionPlaces
+     *            The minimum and maximum number of numerals to display after the decimal separator
+     *            (rounding if too long or padding with zeros if too short).
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static FractionPrecision fixedFraction(int minMaxFractionPlaces) {
+        if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
+        } else {
+            throw new IllegalArgumentException("Fraction length must be between 0 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Always show at least a certain number of fraction places after the decimal separator, padding with
+     * zeros if necessary. Do not perform rounding (display numbers to their full precision).
+     *
+     * <p>
+     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in
+     * {@link #unlimited}.
+     *
+     * @param minFractionPlaces
+     *            The minimum number of numerals to display after the decimal separator (padding with
+     *            zeros if necessary).
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static FractionPrecision minFraction(int minFractionPlaces) {
+        if (minFractionPlaces >= 0 && minFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructFraction(minFractionPlaces, -1);
+        } else {
+            throw new IllegalArgumentException("Fraction length must be between 0 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
+     * decimal separator). Unlike the other fraction rounding strategies, this strategy does <em>not</em>
+     * pad zeros to the end of the number.
+     *
+     * @param maxFractionPlaces
+     *            The maximum number of numerals to display after the decimal mark (rounding if
+     *            necessary).
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static FractionPrecision maxFraction(int maxFractionPlaces) {
+        if (maxFractionPlaces >= 0 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructFraction(0, maxFractionPlaces);
+        } else {
+            throw new IllegalArgumentException("Fraction length must be between 0 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
+     * decimal separator); in addition, always show at least a certain number of places after the decimal
+     * separator, padding with zeros if necessary.
+     *
+     * @param minFractionPlaces
+     *            The minimum number of numerals to display after the decimal separator (padding with
+     *            zeros if necessary).
+     * @param maxFractionPlaces
+     *            The maximum number of numerals to display after the decimal separator (rounding if
+     *            necessary).
+     * @return A FractionPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static FractionPrecision minMaxFraction(int minFractionPlaces, int maxFractionPlaces) {
+        if (minFractionPlaces >= 0
+                && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG
+                && minFractionPlaces <= maxFractionPlaces) {
+            return constructFraction(minFractionPlaces, maxFractionPlaces);
+        } else {
+            throw new IllegalArgumentException("Fraction length must be between 0 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of significant digits or significant
+     * figures. Additionally, pad with zeros to ensure that this number of significant digits/figures are
+     * always shown.
+     *
+     * <p>
+     * This method is equivalent to {@link #minMaxSignificantDigits} with both arguments equal.
+     *
+     * @param minMaxSignificantDigits
+     *            The minimum and maximum number of significant digits to display (rounding if too long
+     *            or padding with zeros if too short).
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision fixedSignificantDigits(int minMaxSignificantDigits) {
+        if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Always show at least a certain number of significant digits/figures, padding with zeros if
+     * necessary. Do not perform rounding (display numbers to their full precision).
+     *
+     * <p>
+     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in
+     * {@link #unlimited}.
+     *
+     * @param minSignificantDigits
+     *            The minimum number of significant digits to display (padding with zeros if too short).
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision minSignificantDigits(int minSignificantDigits) {
+        if (minSignificantDigits >= 1 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructSignificant(minSignificantDigits, -1);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of significant digits/figures.
+     *
+     * @param maxSignificantDigits
+     *            The maximum number of significant digits to display (rounding if too long).
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision maxSignificantDigits(int maxSignificantDigits) {
+        if (maxSignificantDigits >= 1 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
+            return constructSignificant(1, maxSignificantDigits);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition,
+     * always show at least a certain number of significant digits, padding with zeros if necessary.
+     *
+     * @param minSignificantDigits
+     *            The minimum number of significant digits to display (padding with zeros if necessary).
+     * @param maxSignificantDigits
+     *            The maximum number of significant digits to display (rounding if necessary).
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision minMaxSignificantDigits(int minSignificantDigits, int maxSignificantDigits) {
+        if (minSignificantDigits >= 1
+                && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG
+                && minSignificantDigits <= maxSignificantDigits) {
+            return constructSignificant(minSignificantDigits, maxSignificantDigits);
+        } else {
+            throw new IllegalArgumentException("Significant digits must be between 1 and "
+                    + RoundingUtils.MAX_INT_FRAC_SIG
+                    + " (inclusive)");
+        }
+    }
+
+    /**
+     * @deprecated ICU 62 Use *SignificantDigits() instead. This method is for backwards compatibility
+     *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
+     */
+    @Deprecated
+    public static Precision fixedDigits(int a) {
+        return fixedSignificantDigits(a);
+    }
+
+    /**
+     * @deprecated ICU 62 Use *SignificantDigits() instead. This method is for backwards compatibility
+     *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
+     */
+    @Deprecated
+    public static Precision minDigits(int a) {
+        return minSignificantDigits(a);
+    }
+
+    /**
+     * @deprecated ICU 62 Use *SignificantDigits() instead. This method is for backwards compatibility
+     *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
+     */
+    @Deprecated
+    public static Precision maxDigits(int a) {
+        return maxSignificantDigits(a);
+    }
+
+    /**
+     * @deprecated ICU 62 Use *SignificantDigits() instead. This method is for backwards compatibility
+     *             and will be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
+     */
+    @Deprecated
+    public static Precision minMaxDigits(int a, int b) {
+        return minMaxSignificantDigits(a, b);
+    }
+
+    /**
+     * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For
+     * example, if the rounding increment is 0.5, then round 1.2 to 1 and round 1.3 to 1.5.
+     *
+     * <p>
+     * In order to ensure that numbers are padded to the appropriate number of fraction places, set the
+     * scale on the rounding increment BigDecimal. For example, to round to the nearest 0.5 and always
+     * display 2 numerals after the decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you
+     * can run:
+     *
+     * <pre>
+     * Precision.increment(new BigDecimal("0.50"))
+     * </pre>
+     *
+     * <p>
+     * For more information on the scale of Java BigDecimal, see {@link java.math.BigDecimal#scale()}.
+     *
+     * @param roundingIncrement
+     *            The increment to which to round numbers.
+     * @return A Precision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Precision increment(BigDecimal roundingIncrement) {
+        if (roundingIncrement != null && roundingIncrement.compareTo(BigDecimal.ZERO) > 0) {
+            return constructIncrement(roundingIncrement);
+        } else {
+            throw new IllegalArgumentException("Rounding increment must be positive and non-null");
+        }
+    }
+
+    /**
+     * Show numbers rounded and padded according to the rules for the currency unit. The most common
+     * rounding settings for currencies include <code>Precision.fixedFraction(2)</code>,
+     * <code>Precision.integer()</code>, and <code>Precision.increment(0.05)</code> for cash transactions
+     * ("nickel rounding").
+     *
+     * <p>
+     * The exact rounding details will be resolved at runtime based on the currency unit specified in the
+     * NumberFormatter chain. To round according to the rules for one currency while displaying the
+     * symbol for another currency, the withCurrency() method can be called on the return value of this
+     * method.
+     *
+     * @param currencyUsage
+     *            Either STANDARD (for digital transactions) or CASH (for transactions where the rounding
+     *            increment may be limited by the available denominations of cash or coins).
+     * @return A CurrencyPrecision for chaining or passing to the NumberFormatter rounding() setter.
+     * @draft ICU 60
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static CurrencyPrecision currency(CurrencyUsage currencyUsage) {
+        if (currencyUsage != null) {
+            return constructCurrency(currencyUsage);
+        } else {
+            throw new IllegalArgumentException("CurrencyUsage must be non-null");
+        }
+    }
+
+    /**
+     * Sets the {@link java.math.RoundingMode} to use when picking the direction to round (up or down).
+     * Common values include HALF_EVEN, HALF_UP, and FLOOR. The default is HALF_EVEN.
+     *
+     * @param roundingMode
+     *            The RoundingMode to use.
+     * @return A Precision for chaining.
+     * @deprecated ICU 62 Use the top-level rounding mode setting instead. This method is for backwards
+     *             compatibility and will be removed in ICU 64. See
+     *             http://bugs.icu-project.org/trac/ticket/13746
+     * @see NumberFormatter
+     */
+    @Deprecated
+    public Precision withMode(RoundingMode roundingMode) {
+        return withMode(RoundingUtils.mathContextUnlimited(roundingMode));
+    }
+
+    /**
+     * Sets a MathContext directly instead of RoundingMode.
+     *
+     * @internal
+     * @deprecated This API is ICU internal only.
+     */
+    @Deprecated
+    public Precision withMode(MathContext mathContext) {
+        if (this.mathContext.equals(mathContext)) {
+            return this;
+        }
+        Precision other = (Precision) this.clone();
+        other.mathContext = mathContext;
+        return other;
+    }
+
+    /**
+     * @internal
+     * @deprecated This API is ICU internal only.
+     */
+    @Deprecated
+    @Override
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            // Should not happen since parent is Object
+            throw new AssertionError(e);
+        }
+    }
+
+    /**
+     * @internal
+     * @deprecated ICU 60 This API is ICU internal only.
+     */
+    @Deprecated
+    public abstract void apply(DecimalQuantity value);
+
+    //////////////////////////
+    // PACKAGE-PRIVATE APIS //
+    //////////////////////////
+
+    static final InfiniteRounderImpl NONE = new InfiniteRounderImpl();
+
+    static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0);
+    static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2);
+    static final FractionRounderImpl DEFAULT_MAX_FRAC_6 = new FractionRounderImpl(0, 6);
+
+    static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2);
+    static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3);
+    static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3);
+
+    static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1);
+
+    static final IncrementRounderImpl NICKEL = new IncrementRounderImpl(BigDecimal.valueOf(0.05));
+
+    static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD);
+    static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH);
+
+    static final PassThroughRounderImpl PASS_THROUGH = new PassThroughRounderImpl();
+
+    static Precision constructInfinite() {
+        return NONE;
+    }
+
+    static FractionPrecision constructFraction(int minFrac, int maxFrac) {
+        if (minFrac == 0 && maxFrac == 0) {
+            return FIXED_FRAC_0;
+        } else if (minFrac == 2 && maxFrac == 2) {
+            return FIXED_FRAC_2;
+        } else if (minFrac == 0 && maxFrac == 6) {
+            return DEFAULT_MAX_FRAC_6;
+        } else {
+            return new FractionRounderImpl(minFrac, maxFrac);
+        }
+    }
+
+    /** Assumes that minSig <= maxSig. */
+    static Precision constructSignificant(int minSig, int maxSig) {
+        if (minSig == 2 && maxSig == 2) {
+            return FIXED_SIG_2;
+        } else if (minSig == 3 && maxSig == 3) {
+            return FIXED_SIG_3;
+        } else if (minSig == 2 && maxSig == 3) {
+            return RANGE_SIG_2_3;
+        } else {
+            return new SignificantRounderImpl(minSig, maxSig);
+        }
+    }
+
+    static Precision constructFractionSignificant(FractionPrecision base_, int minSig, int maxSig) {
+        assert base_ instanceof FractionRounderImpl;
+        FractionRounderImpl base = (FractionRounderImpl) base_;
+        if (base.minFrac == 0 && base.maxFrac == 0 && minSig == 2 /* && maxSig == -1 */) {
+            return COMPACT_STRATEGY;
+        } else {
+            return new FracSigRounderImpl(base.minFrac, base.maxFrac, minSig, maxSig);
+        }
+    }
+
+    static Precision constructIncrement(BigDecimal increment) {
+        // NOTE: .equals() is what we want, not .compareTo()
+        if (increment.equals(NICKEL.increment)) {
+            return NICKEL;
+        } else {
+            return new IncrementRounderImpl(increment);
+        }
+    }
+
+    static CurrencyPrecision constructCurrency(CurrencyUsage usage) {
+        if (usage == CurrencyUsage.STANDARD) {
+            return MONETARY_STANDARD;
+        } else if (usage == CurrencyUsage.CASH) {
+            return MONETARY_CASH;
+        } else {
+            throw new AssertionError();
+        }
+    }
+
+    static Precision constructFromCurrency(CurrencyPrecision base_, Currency currency) {
+        assert base_ instanceof CurrencyRounderImpl;
+        CurrencyRounderImpl base = (CurrencyRounderImpl) base_;
+        double incrementDouble = currency.getRoundingIncrement(base.usage);
+        if (incrementDouble != 0.0) {
+            BigDecimal increment = BigDecimal.valueOf(incrementDouble);
+            return constructIncrement(increment);
+        } else {
+            int minMaxFrac = currency.getDefaultFractionDigits(base.usage);
+            return constructFraction(minMaxFrac, minMaxFrac);
+        }
+    }
+
+    static Precision constructPassThrough() {
+        return PASS_THROUGH;
+    }
+
+    /**
+     * Returns a valid working Rounder. If the Rounder is a CurrencyRounder, applies the given currency.
+     * Otherwise, simply passes through the argument.
+     *
+     * @param currency
+     *            A currency object to use in case the input object needs it.
+     * @return A Rounder object ready for use.
+     */
+    Precision withLocaleData(Currency currency) {
+        if (this instanceof CurrencyPrecision) {
+            return ((CurrencyPrecision) this).withCurrency(currency);
+        } else {
+            return this;
+        }
+    }
+
+    /**
+     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate
+     * multiplier (magnitude adjustment), applies the adjustment, rounds, and returns the chosen
+     * multiplier.
+     *
+     * <p>
+     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier
+     * boundary, we need to re-do the rounding. For example, to display 999,999 in Engineering notation
+     * with 2 sigfigs, first you guess the multiplier to be -3. However, then you end up getting 1000E3,
+     * which is not the correct output. You then change your multiplier to be -6, and you get 1.0E6,
+     * which is correct.
+     *
+     * @param input
+     *            The quantity to process.
+     * @param producer
+     *            Function to call to return a multiplier based on a magnitude.
+     * @return The number of orders of magnitude the input was adjusted by this method.
+     */
+    int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) {
+        // Do not call this method with zero.
+        assert !input.isZero();
+
+        // Perform the first attempt at rounding.
+        int magnitude = input.getMagnitude();
+        int multiplier = producer.getMultiplier(magnitude);
+        input.adjustMagnitude(multiplier);
+        apply(input);
+
+        // If the number rounded to zero, exit.
+        if (input.isZero()) {
+            return multiplier;
+        }
+
+        // If the new magnitude after rounding is the same as it was before rounding, then we are done.
+        // This case applies to most numbers.
+        if (input.getMagnitude() == magnitude + multiplier) {
+            return multiplier;
+        }
+
+        // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
+        // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
+        // we do not need to make any more adjustments.
+        int _multiplier = producer.getMultiplier(magnitude + 1);
+        if (multiplier == _multiplier) {
+            return multiplier;
+        }
+
+        // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
+        // Fix the magnitude and re-apply the rounding strategy.
+        input.adjustMagnitude(_multiplier - multiplier);
+        apply(input);
+        return _multiplier;
+    }
+
+    ///////////////
+    // INTERNALS //
+    ///////////////
+
+    static class InfiniteRounderImpl extends Precision {
+
+        public InfiniteRounderImpl() {
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            value.roundToInfinity();
+            value.setFractionLength(0, Integer.MAX_VALUE);
+        }
+    }
+
+    static class FractionRounderImpl extends FractionPrecision {
+        final int minFrac;
+        final int maxFrac;
+
+        public FractionRounderImpl(int minFrac, int maxFrac) {
+            this.minFrac = minFrac;
+            this.maxFrac = maxFrac;
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            value.roundToMagnitude(getRoundingMagnitudeFraction(maxFrac), mathContext);
+            value.setFractionLength(Math.max(0, -getDisplayMagnitudeFraction(minFrac)),
+                    Integer.MAX_VALUE);
+        }
+    }
+
+    static class SignificantRounderImpl extends Precision {
+        final int minSig;
+        final int maxSig;
+
+        public SignificantRounderImpl(int minSig, int maxSig) {
+            this.minSig = minSig;
+            this.maxSig = maxSig;
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext);
+            value.setFractionLength(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)),
+                    Integer.MAX_VALUE);
+            // Make sure that digits are displayed on zero.
+            if (value.isZero() && minSig > 0) {
+                value.setIntegerLength(1, Integer.MAX_VALUE);
+            }
+        }
+
+        /**
+         * Version of {@link #apply} that obeys minInt constraints. Used for scientific notation
+         * compatibility mode.
+         */
+        public void apply(DecimalQuantity quantity, int minInt) {
+            assert quantity.isZero();
+            quantity.setFractionLength(minSig - minInt, Integer.MAX_VALUE);
+        }
+    }
+
+    static class FracSigRounderImpl extends Precision {
+        final int minFrac;
+        final int maxFrac;
+        final int minSig;
+        final int maxSig;
+
+        public FracSigRounderImpl(int minFrac, int maxFrac, int minSig, int maxSig) {
+            this.minFrac = minFrac;
+            this.maxFrac = maxFrac;
+            this.minSig = minSig;
+            this.maxSig = maxSig;
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            int displayMag = getDisplayMagnitudeFraction(minFrac);
+            int roundingMag = getRoundingMagnitudeFraction(maxFrac);
+            if (minSig == -1) {
+                // Max Sig override
+                int candidate = getRoundingMagnitudeSignificant(value, maxSig);
+                roundingMag = Math.max(roundingMag, candidate);
+            } else {
+                // Min Sig override
+                int candidate = getDisplayMagnitudeSignificant(value, minSig);
+                roundingMag = Math.min(roundingMag, candidate);
+            }
+            value.roundToMagnitude(roundingMag, mathContext);
+            value.setFractionLength(Math.max(0, -displayMag), Integer.MAX_VALUE);
+        }
+    }
+
+    static class IncrementRounderImpl extends Precision {
+        final BigDecimal increment;
+
+        public IncrementRounderImpl(BigDecimal increment) {
+            this.increment = increment;
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            value.roundToIncrement(increment, mathContext);
+            value.setFractionLength(increment.scale(), increment.scale());
+        }
+    }
+
+    static class CurrencyRounderImpl extends CurrencyPrecision {
+        final CurrencyUsage usage;
+
+        public CurrencyRounderImpl(CurrencyUsage usage) {
+            this.usage = usage;
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            // Call .withCurrency() before .apply()!
+            throw new AssertionError();
+        }
+    }
+
+    static class PassThroughRounderImpl extends Precision {
+
+        public PassThroughRounderImpl() {
+        }
+
+        @Override
+        public void apply(DecimalQuantity value) {
+            // TODO: Assert that value has already been rounded
+        }
+    }
+
+    private static int getRoundingMagnitudeFraction(int maxFrac) {
+        if (maxFrac == -1) {
+            return Integer.MIN_VALUE;
+        }
+        return -maxFrac;
+    }
+
+    private static int getRoundingMagnitudeSignificant(DecimalQuantity value, int maxSig) {
+        if (maxSig == -1) {
+            return Integer.MIN_VALUE;
+        }
+        int magnitude = value.isZero() ? 0 : value.getMagnitude();
+        return magnitude - maxSig + 1;
+    }
+
+    private static int getDisplayMagnitudeFraction(int minFrac) {
+        if (minFrac == 0) {
+            return Integer.MAX_VALUE;
+        }
+        return -minFrac;
+    }
+
+    private static int getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig) {
+        int magnitude = value.isZero() ? 0 : value.getMagnitude();
+        return magnitude - minSig + 1;
+    }
+}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/Rounder.java b/icu4j/main/classes/core/src/com/ibm/icu/number/Rounder.java
index d38f99e926b..f9db169e364 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/Rounder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/Rounder.java
@@ -1,721 +1,11 @@
-// © 2017 and later: Unicode, Inc. and others.
+// © 2018 and later: Unicode, Inc. and others.
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-
-import com.ibm.icu.impl.number.DecimalQuantity;
-import com.ibm.icu.impl.number.MultiplierProducer;
-import com.ibm.icu.impl.number.RoundingUtils;
-import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-
 /**
- * A class that defines the rounding strategy to be used when formatting numbers in NumberFormatter.
- *
- * <p>
- * To create a Rounder, use one of the factory methods.
- *
- * @draft ICU 60
- * @provisional This API might change or be removed in a future release.
- * @see NumberFormatter
+ * @deprecated ICU 62 Use {@link Precision} instead. This class is for backwards compatibility and will
+ *             be removed in ICU 64. See http://bugs.icu-project.org/trac/ticket/13746
  */
-public abstract class Rounder implements Cloneable {
-
-    /* package-private final */ MathContext mathContext;
-
-    /* package-private */ Rounder() {
-        mathContext = RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED;
-    }
-
-    /**
-     * Show all available digits to full precision.
-     *
-     * <p>
-     * <strong>NOTE:</strong> When formatting a <em>double</em>, this method, along with
-     * {@link #minFraction} and {@link #minDigits}, will trigger complex algorithm similar to
-     * <em>Dragon4</em> to determine the low-order digits and the number of digits to display based on
-     * the value of the double. If the number of fraction places or significant digits can be bounded,
-     * consider using {@link #maxFraction} or {@link #maxDigits} instead to maximize performance. For
-     * more information, read the following blog post.
-     *
-     * <p>
-     * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/
-     *
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder unlimited() {
-        return constructInfinite();
-    }
-
-    /**
-     * Show numbers rounded if necessary to the nearest integer.
-     *
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static FractionRounder integer() {
-        return constructFraction(0, 0);
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
-     * decimal separator). Additionally, pad with zeros to ensure that this number of places are always
-     * shown.
-     *
-     * <p>
-     * Example output with minMaxFractionPlaces = 3:
-     *
-     * <p>
-     * 87,650.000<br>
-     * 8,765.000<br>
-     * 876.500<br>
-     * 87.650<br>
-     * 8.765<br>
-     * 0.876<br>
-     * 0.088<br>
-     * 0.009<br>
-     * 0.000 (zero)
-     *
-     * <p>
-     * This method is equivalent to {@link #minMaxFraction} with both arguments equal.
-     *
-     * @param minMaxFractionPlaces
-     *            The minimum and maximum number of numerals to display after the decimal separator
-     *            (rounding if too long or padding with zeros if too short).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static FractionRounder fixedFraction(int minMaxFractionPlaces) {
-        if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
-        } else {
-            throw new IllegalArgumentException("Fraction length must be between 0 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Always show at least a certain number of fraction places after the decimal separator, padding with
-     * zeros if necessary. Do not perform rounding (display numbers to their full precision).
-     *
-     * <p>
-     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in
-     * {@link #unlimited}.
-     *
-     * @param minFractionPlaces
-     *            The minimum number of numerals to display after the decimal separator (padding with
-     *            zeros if necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static FractionRounder minFraction(int minFractionPlaces) {
-        if (minFractionPlaces >= 0 && minFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructFraction(minFractionPlaces, -1);
-        } else {
-            throw new IllegalArgumentException("Fraction length must be between 0 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
-     * decimal separator). Unlike the other fraction rounding strategies, this strategy does <em>not</em>
-     * pad zeros to the end of the number.
-     *
-     * @param maxFractionPlaces
-     *            The maximum number of numerals to display after the decimal mark (rounding if
-     *            necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static FractionRounder maxFraction(int maxFractionPlaces) {
-        if (maxFractionPlaces >= 0 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructFraction(0, maxFractionPlaces);
-        } else {
-            throw new IllegalArgumentException("Fraction length must be between 0 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of fraction places (numerals after the
-     * decimal separator); in addition, always show at least a certain number of places after the decimal
-     * separator, padding with zeros if necessary.
-     *
-     * @param minFractionPlaces
-     *            The minimum number of numerals to display after the decimal separator (padding with
-     *            zeros if necessary).
-     * @param maxFractionPlaces
-     *            The maximum number of numerals to display after the decimal separator (rounding if
-     *            necessary).
-     * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static FractionRounder minMaxFraction(int minFractionPlaces, int maxFractionPlaces) {
-        if (minFractionPlaces >= 0
-                && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG
-                && minFractionPlaces <= maxFractionPlaces) {
-            return constructFraction(minFractionPlaces, maxFractionPlaces);
-        } else {
-            throw new IllegalArgumentException("Fraction length must be between 0 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of significant digits or significant
-     * figures. Additionally, pad with zeros to ensure that this number of significant digits/figures are
-     * always shown.
-     *
-     * <p>
-     * This method is equivalent to {@link #minMaxDigits} with both arguments equal.
-     *
-     * @param minMaxSignificantDigits
-     *            The minimum and maximum number of significant digits to display (rounding if too long
-     *            or padding with zeros if too short).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder fixedDigits(int minMaxSignificantDigits) {
-        if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Always show at least a certain number of significant digits/figures, padding with zeros if
-     * necessary. Do not perform rounding (display numbers to their full precision).
-     *
-     * <p>
-     * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in
-     * {@link #unlimited}.
-     *
-     * @param minSignificantDigits
-     *            The minimum number of significant digits to display (padding with zeros if too short).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder minDigits(int minSignificantDigits) {
-        if (minSignificantDigits >= 1 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructSignificant(minSignificantDigits, -1);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of significant digits/figures.
-     *
-     * @param maxSignificantDigits
-     *            The maximum number of significant digits to display (rounding if too long).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder maxDigits(int maxSignificantDigits) {
-        if (maxSignificantDigits >= 1 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
-            return constructSignificant(1, maxSignificantDigits);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition,
-     * always show at least a certain number of significant digits, padding with zeros if necessary.
-     *
-     * @param minSignificantDigits
-     *            The minimum number of significant digits to display (padding with zeros if necessary).
-     * @param maxSignificantDigits
-     *            The maximum number of significant digits to display (rounding if necessary).
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder minMaxDigits(int minSignificantDigits, int maxSignificantDigits) {
-        if (minSignificantDigits >= 1
-                && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG
-                && minSignificantDigits <= maxSignificantDigits) {
-            return constructSignificant(minSignificantDigits, maxSignificantDigits);
-        } else {
-            throw new IllegalArgumentException("Significant digits must be between 1 and "
-                    + RoundingUtils.MAX_INT_FRAC_SIG
-                    + " (inclusive)");
-        }
-    }
-
-    /**
-     * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For
-     * example, if the rounding increment is 0.5, then round 1.2 to 1 and round 1.3 to 1.5.
-     *
-     * <p>
-     * In order to ensure that numbers are padded to the appropriate number of fraction places, set the
-     * scale on the rounding increment BigDecimal. For example, to round to the nearest 0.5 and always
-     * display 2 numerals after the decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you
-     * can run:
-     *
-     * <pre>
-     * Rounder.increment(new BigDecimal("0.50"))
-     * </pre>
-     *
-     * <p>
-     * For more information on the scale of Java BigDecimal, see {@link java.math.BigDecimal#scale()}.
-     *
-     * @param roundingIncrement
-     *            The increment to which to round numbers.
-     * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static Rounder increment(BigDecimal roundingIncrement) {
-        if (roundingIncrement != null && roundingIncrement.compareTo(BigDecimal.ZERO) > 0) {
-            return constructIncrement(roundingIncrement);
-        } else {
-            throw new IllegalArgumentException("Rounding increment must be positive and non-null");
-        }
-    }
-
-    /**
-     * Show numbers rounded and padded according to the rules for the currency unit. The most common
-     * rounding settings for currencies include <code>Rounder.fixedFraction(2)</code>,
-     * <code>Rounder.integer()</code>, and <code>Rounder.increment(0.05)</code> for cash transactions
-     * ("nickel rounding").
-     *
-     * <p>
-     * The exact rounding details will be resolved at runtime based on the currency unit specified in the
-     * NumberFormatter chain. To round according to the rules for one currency while displaying the
-     * symbol for another currency, the withCurrency() method can be called on the return value of this
-     * method.
-     *
-     * @param currencyUsage
-     *            Either STANDARD (for digital transactions) or CASH (for transactions where the rounding
-     *            increment may be limited by the available denominations of cash or coins).
-     * @return A CurrencyRounder for chaining or passing to the NumberFormatter rounding() setter.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public static CurrencyRounder currency(CurrencyUsage currencyUsage) {
-        if (currencyUsage != null) {
-            return constructCurrency(currencyUsage);
-        } else {
-            throw new IllegalArgumentException("CurrencyUsage must be non-null");
-        }
-    }
-
-    /**
-     * Sets the {@link java.math.RoundingMode} to use when picking the direction to round (up or down).
-     * Common values include HALF_EVEN, HALF_UP, and FLOOR. The default is HALF_EVEN.
-     *
-     * @param roundingMode
-     *            The RoundingMode to use.
-     * @return A Rounder for chaining.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
-     * @see NumberFormatter
-     */
-    public Rounder withMode(RoundingMode roundingMode) {
-        return withMode(RoundingUtils.mathContextUnlimited(roundingMode));
-    }
-
-    /**
-     * Sets a MathContext directly instead of RoundingMode.
-     *
-     * @internal
-     * @deprecated This API is ICU internal only.
-     */
-    @Deprecated
-    public Rounder withMode(MathContext mathContext) {
-        if (this.mathContext.equals(mathContext)) {
-            return this;
-        }
-        Rounder other = (Rounder) this.clone();
-        other.mathContext = mathContext;
-        return other;
-    }
-
-    /**
-     * @internal
-     * @deprecated This API is ICU internal only.
-     */
-    @Deprecated
-    @Override
-    public Object clone() {
-        try {
-            return super.clone();
-        } catch (CloneNotSupportedException e) {
-            // Should not happen since parent is Object
-            throw new AssertionError(e);
-        }
-    }
-
-    /**
-     * @internal
-     * @deprecated ICU 60 This API is ICU internal only.
-     */
-    @Deprecated
-    public abstract void apply(DecimalQuantity value);
-
-    //////////////////////////
-    // PACKAGE-PRIVATE APIS //
-    //////////////////////////
-
-    static final InfiniteRounderImpl NONE = new InfiniteRounderImpl();
-
-    static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0);
-    static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2);
-    static final FractionRounderImpl DEFAULT_MAX_FRAC_6 = new FractionRounderImpl(0, 6);
-
-    static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2);
-    static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3);
-    static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3);
-
-    static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1);
-
-    static final IncrementRounderImpl NICKEL = new IncrementRounderImpl(BigDecimal.valueOf(0.05));
-
-    static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD);
-    static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH);
-
-    static final PassThroughRounderImpl PASS_THROUGH = new PassThroughRounderImpl();
-
-    static Rounder constructInfinite() {
-        return NONE;
-    }
-
-    static FractionRounder constructFraction(int minFrac, int maxFrac) {
-        if (minFrac == 0 && maxFrac == 0) {
-            return FIXED_FRAC_0;
-        } else if (minFrac == 2 && maxFrac == 2) {
-            return FIXED_FRAC_2;
-        } else if (minFrac == 0 && maxFrac == 6) {
-            return DEFAULT_MAX_FRAC_6;
-        } else {
-            return new FractionRounderImpl(minFrac, maxFrac);
-        }
-    }
-
-    /** Assumes that minSig <= maxSig. */
-    static Rounder constructSignificant(int minSig, int maxSig) {
-        if (minSig == 2 && maxSig == 2) {
-            return FIXED_SIG_2;
-        } else if (minSig == 3 && maxSig == 3) {
-            return FIXED_SIG_3;
-        } else if (minSig == 2 && maxSig == 3) {
-            return RANGE_SIG_2_3;
-        } else {
-            return new SignificantRounderImpl(minSig, maxSig);
-        }
-    }
-
-    static Rounder constructFractionSignificant(FractionRounder base_, int minSig, int maxSig) {
-        assert base_ instanceof FractionRounderImpl;
-        FractionRounderImpl base = (FractionRounderImpl) base_;
-        if (base.minFrac == 0 && base.maxFrac == 0 && minSig == 2 /* && maxSig == -1 */) {
-            return COMPACT_STRATEGY;
-        } else {
-            return new FracSigRounderImpl(base.minFrac, base.maxFrac, minSig, maxSig);
-        }
-    }
-
-    static Rounder constructIncrement(BigDecimal increment) {
-        // NOTE: .equals() is what we want, not .compareTo()
-        if (increment.equals(NICKEL.increment)) {
-            return NICKEL;
-        } else {
-            return new IncrementRounderImpl(increment);
-        }
-    }
-
-    static CurrencyRounder constructCurrency(CurrencyUsage usage) {
-        if (usage == CurrencyUsage.STANDARD) {
-            return MONETARY_STANDARD;
-        } else if (usage == CurrencyUsage.CASH) {
-            return MONETARY_CASH;
-        } else {
-            throw new AssertionError();
-        }
-    }
-
-    static Rounder constructFromCurrency(CurrencyRounder base_, Currency currency) {
-        assert base_ instanceof CurrencyRounderImpl;
-        CurrencyRounderImpl base = (CurrencyRounderImpl) base_;
-        double incrementDouble = currency.getRoundingIncrement(base.usage);
-        if (incrementDouble != 0.0) {
-            BigDecimal increment = BigDecimal.valueOf(incrementDouble);
-            return constructIncrement(increment);
-        } else {
-            int minMaxFrac = currency.getDefaultFractionDigits(base.usage);
-            return constructFraction(minMaxFrac, minMaxFrac);
-        }
-    }
-
-    static Rounder constructPassThrough() {
-        return PASS_THROUGH;
-    }
-
-    /**
-     * Returns a valid working Rounder. If the Rounder is a CurrencyRounder, applies the given currency.
-     * Otherwise, simply passes through the argument.
-     *
-     * @param currency
-     *            A currency object to use in case the input object needs it.
-     * @return A Rounder object ready for use.
-     */
-    Rounder withLocaleData(Currency currency) {
-        if (this instanceof CurrencyRounder) {
-            return ((CurrencyRounder) this).withCurrency(currency);
-        } else {
-            return this;
-        }
-    }
-
-    /**
-     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate
-     * multiplier (magnitude adjustment), applies the adjustment, rounds, and returns the chosen
-     * multiplier.
-     *
-     * <p>
-     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier
-     * boundary, we need to re-do the rounding. For example, to display 999,999 in Engineering notation
-     * with 2 sigfigs, first you guess the multiplier to be -3. However, then you end up getting 1000E3,
-     * which is not the correct output. You then change your multiplier to be -6, and you get 1.0E6,
-     * which is correct.
-     *
-     * @param input
-     *            The quantity to process.
-     * @param producer
-     *            Function to call to return a multiplier based on a magnitude.
-     * @return The number of orders of magnitude the input was adjusted by this method.
-     */
-    int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) {
-        // Do not call this method with zero.
-        assert !input.isZero();
-
-        // Perform the first attempt at rounding.
-        int magnitude = input.getMagnitude();
-        int multiplier = producer.getMultiplier(magnitude);
-        input.adjustMagnitude(multiplier);
-        apply(input);
-
-        // If the number rounded to zero, exit.
-        if (input.isZero()) {
-            return multiplier;
-        }
-
-        // If the new magnitude after rounding is the same as it was before rounding, then we are done.
-        // This case applies to most numbers.
-        if (input.getMagnitude() == magnitude + multiplier) {
-            return multiplier;
-        }
-
-        // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
-        // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
-        // we do not need to make any more adjustments.
-        int _multiplier = producer.getMultiplier(magnitude + 1);
-        if (multiplier == _multiplier) {
-            return multiplier;
-        }
-
-        // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
-        // Fix the magnitude and re-apply the rounding strategy.
-        input.adjustMagnitude(_multiplier - multiplier);
-        apply(input);
-        return _multiplier;
-    }
-
-    ///////////////
-    // INTERNALS //
-    ///////////////
-
-    static class InfiniteRounderImpl extends Rounder {
-
-        public InfiniteRounderImpl() {
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            value.roundToInfinity();
-            value.setFractionLength(0, Integer.MAX_VALUE);
-        }
-    }
-
-    static class FractionRounderImpl extends FractionRounder {
-        final int minFrac;
-        final int maxFrac;
-
-        public FractionRounderImpl(int minFrac, int maxFrac) {
-            this.minFrac = minFrac;
-            this.maxFrac = maxFrac;
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            value.roundToMagnitude(getRoundingMagnitudeFraction(maxFrac), mathContext);
-            value.setFractionLength(Math.max(0, -getDisplayMagnitudeFraction(minFrac)),
-                    Integer.MAX_VALUE);
-        }
-    }
-
-    static class SignificantRounderImpl extends Rounder {
-        final int minSig;
-        final int maxSig;
-
-        public SignificantRounderImpl(int minSig, int maxSig) {
-            this.minSig = minSig;
-            this.maxSig = maxSig;
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext);
-            value.setFractionLength(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)),
-                    Integer.MAX_VALUE);
-            // Make sure that digits are displayed on zero.
-            if (value.isZero() && minSig > 0) {
-                value.setIntegerLength(1, Integer.MAX_VALUE);
-            }
-        }
-
-        /**
-         * Version of {@link #apply} that obeys minInt constraints. Used for scientific notation
-         * compatibility mode.
-         */
-        public void apply(DecimalQuantity quantity, int minInt) {
-            assert quantity.isZero();
-            quantity.setFractionLength(minSig - minInt, Integer.MAX_VALUE);
-        }
-    }
-
-    static class FracSigRounderImpl extends Rounder {
-        final int minFrac;
-        final int maxFrac;
-        final int minSig;
-        final int maxSig;
-
-        public FracSigRounderImpl(int minFrac, int maxFrac, int minSig, int maxSig) {
-            this.minFrac = minFrac;
-            this.maxFrac = maxFrac;
-            this.minSig = minSig;
-            this.maxSig = maxSig;
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            int displayMag = getDisplayMagnitudeFraction(minFrac);
-            int roundingMag = getRoundingMagnitudeFraction(maxFrac);
-            if (minSig == -1) {
-                // Max Sig override
-                int candidate = getRoundingMagnitudeSignificant(value, maxSig);
-                roundingMag = Math.max(roundingMag, candidate);
-            } else {
-                // Min Sig override
-                int candidate = getDisplayMagnitudeSignificant(value, minSig);
-                roundingMag = Math.min(roundingMag, candidate);
-            }
-            value.roundToMagnitude(roundingMag, mathContext);
-            value.setFractionLength(Math.max(0, -displayMag), Integer.MAX_VALUE);
-        }
-    }
-
-    static class IncrementRounderImpl extends Rounder {
-        final BigDecimal increment;
-
-        public IncrementRounderImpl(BigDecimal increment) {
-            this.increment = increment;
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            value.roundToIncrement(increment, mathContext);
-            value.setFractionLength(increment.scale(), increment.scale());
-        }
-    }
-
-    static class CurrencyRounderImpl extends CurrencyRounder {
-        final CurrencyUsage usage;
-
-        public CurrencyRounderImpl(CurrencyUsage usage) {
-            this.usage = usage;
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            // Call .withCurrency() before .apply()!
-            throw new AssertionError();
-        }
-    }
-
-    static class PassThroughRounderImpl extends Rounder {
-
-        public PassThroughRounderImpl() {
-        }
-
-        @Override
-        public void apply(DecimalQuantity value) {
-            // TODO: Assert that value has already been rounded
-        }
-    }
-
-    private static int getRoundingMagnitudeFraction(int maxFrac) {
-        if (maxFrac == -1) {
-            return Integer.MIN_VALUE;
-        }
-        return -maxFrac;
-    }
-
-    private static int getRoundingMagnitudeSignificant(DecimalQuantity value, int maxSig) {
-        if (maxSig == -1) {
-            return Integer.MIN_VALUE;
-        }
-        int magnitude = value.isZero() ? 0 : value.getMagnitude();
-        return magnitude - maxSig + 1;
-    }
-
-    private static int getDisplayMagnitudeFraction(int minFrac) {
-        if (minFrac == 0) {
-            return Integer.MAX_VALUE;
-        }
-        return -minFrac;
-    }
-
-    private static int getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig) {
-        int magnitude = value.isZero() ? 0 : value.getMagnitude();
-        return magnitude - minSig + 1;
-    }
+@Deprecated
+public abstract class Rounder extends Precision {
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java b/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
index e121fb7db81..d3f62b963e3 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
@@ -10,7 +10,7 @@ import com.ibm.icu.impl.number.MultiplierProducer;
 import com.ibm.icu.impl.number.NumberStringBuilder;
 import com.ibm.icu.impl.number.RoundingUtils;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
-import com.ibm.icu.number.Rounder.SignificantRounderImpl;
+import com.ibm.icu.number.Precision.SignificantRounderImpl;
 import com.ibm.icu.text.DecimalFormatSymbols;
 import com.ibm.icu.text.NumberFormat;
 
@@ -159,22 +159,22 @@ public class ScientificNotation extends Notation implements Cloneable {
         @Override
         public MicroProps processQuantity(DecimalQuantity quantity) {
             MicroProps micros = parent.processQuantity(quantity);
-            assert micros.rounding != null;
+            assert micros.rounder != null;
 
             // Treat zero as if it had magnitude 0
             int exponent;
             if (quantity.isZero()) {
-                if (notation.requireMinInt && micros.rounding instanceof SignificantRounderImpl) {
+                if (notation.requireMinInt && micros.rounder instanceof SignificantRounderImpl) {
                     // Show "00.000E0" on pattern "00.000E0"
-                    ((SignificantRounderImpl) micros.rounding).apply(quantity,
+                    ((SignificantRounderImpl) micros.rounder).apply(quantity,
                             notation.engineeringInterval);
                     exponent = 0;
                 } else {
-                    micros.rounding.apply(quantity);
+                    micros.rounder.apply(quantity);
                     exponent = 0;
                 }
             } else {
-                exponent = -micros.rounding.chooseMultiplierAndApply(quantity, this);
+                exponent = -micros.rounder.chooseMultiplierAndApply(quantity, this);
             }
 
             // Add the Modifier for the scientific format.
@@ -191,7 +191,7 @@ public class ScientificNotation extends Notation implements Cloneable {
             }
 
             // We already performed rounding. Do not perform it again.
-            micros.rounding = Rounder.constructPassThrough();
+            micros.rounder = Precision.constructPassThrough();
 
             return micros;
         }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
index 0fe0220adc0..175d92e8d40 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
@@ -40,7 +40,7 @@ import com.ibm.icu.number.FormattedNumber;
 import com.ibm.icu.number.LocalizedNumberFormatter;
 import com.ibm.icu.number.NumberFormatter;
 import com.ibm.icu.number.NumberFormatter.UnitWidth;
-import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.Precision;
 import com.ibm.icu.text.ListFormatter.FormattedListBuilder;
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.ICUUncheckedIOException;
@@ -738,7 +738,7 @@ public class MeasureFormat extends UFormat {
         } else {
             assert type == NUMBER_FORMATTER_INTEGER;
             formatter = getNumberFormatter().unit(unit).perUnit(perUnit).unitWidth(formatWidth.unitWidth)
-                    .rounding(Rounder.integer().withMode(RoundingMode.DOWN));
+                    .rounding(Precision.integer().withMode(RoundingMode.DOWN));
         }
         formatter3 = formatter2;
         formatter2 = formatter1;
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ExhaustiveNumberTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ExhaustiveNumberTest.java
index b924b0525b3..bca8f848a6a 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ExhaustiveNumberTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ExhaustiveNumberTest.java
@@ -15,7 +15,7 @@ import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
 import com.ibm.icu.impl.number.parse.UnicodeSetStaticCache.Key;
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.number.NumberFormatter;
-import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.Precision;
 import com.ibm.icu.text.DecimalFormatSymbols;
 import com.ibm.icu.text.UnicodeSet;
 import com.ibm.icu.util.ULocale;
@@ -92,7 +92,7 @@ public class ExhaustiveNumberTest extends TestFmwk {
         BigDecimal ten10000 = BigDecimal.valueOf(10).pow(10000);
         BigDecimal longFraction = ten10000.subtract(BigDecimal.ONE).divide(ten10000);
         String expected = longFraction.toPlainString();
-        String actual = NumberFormatter.withLocale(ULocale.ENGLISH).rounding(Rounder.unlimited())
+        String actual = NumberFormatter.withLocale(ULocale.ENGLISH).rounding(Precision.unlimited())
                 .format(longFraction).toString();
         assertEquals("All digits should be displayed", expected, actual);
     }
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
index 6a07b30d505..5b99460b8fe 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -30,7 +30,7 @@ import com.ibm.icu.impl.number.Padder.PadPosition;
 import com.ibm.icu.impl.number.PatternStringParser;
 import com.ibm.icu.number.CompactNotation;
 import com.ibm.icu.number.FormattedNumber;
-import com.ibm.icu.number.FractionRounder;
+import com.ibm.icu.number.FractionPrecision;
 import com.ibm.icu.number.IntegerWidth;
 import com.ibm.icu.number.LocalizedNumberFormatter;
 import com.ibm.icu.number.Notation;
@@ -39,7 +39,7 @@ import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.GroupingStrategy;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
 import com.ibm.icu.number.NumberFormatter.UnitWidth;
-import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.Precision;
 import com.ibm.icu.number.Scale;
 import com.ibm.icu.number.ScientificNotation;
 import com.ibm.icu.number.UnlocalizedNumberFormatter;
@@ -510,7 +510,7 @@ public class NumberFormatterApiTest {
                 "MeasureUnit form without {0} in CLDR pattern and wide base form",
                 "measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
                 NumberFormatter.with()
-                    .rounding(Rounder.fixedFraction(20))
+                    .precision(Precision.fixedFraction(20))
                     .unit(MeasureUnit.KELVIN)
                     .unitWidth(UnitWidth.FULL_NAME),
                 ULocale.forLanguageTag("es-MX"),
@@ -777,8 +777,8 @@ public class NumberFormatterApiTest {
     public void roundingFraction() {
         assertFormatDescending(
                 "Integer",
-                "round-integer",
-                NumberFormatter.with().rounding(Rounder.integer()),
+                "precision-integer",
+                NumberFormatter.with().precision(Precision.integer()),
                 ULocale.ENGLISH,
                 "87,650",
                 "8,765",
@@ -793,7 +793,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "Fixed Fraction",
                 ".000",
-                NumberFormatter.with().rounding(Rounder.fixedFraction(3)),
+                NumberFormatter.with().precision(Precision.fixedFraction(3)),
                 ULocale.ENGLISH,
                 "87,650.000",
                 "8,765.000",
@@ -808,7 +808,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "Min Fraction",
                 ".0+",
-                NumberFormatter.with().rounding(Rounder.minFraction(1)),
+                NumberFormatter.with().precision(Precision.minFraction(1)),
                 ULocale.ENGLISH,
                 "87,650.0",
                 "8,765.0",
@@ -823,7 +823,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "Max Fraction",
                 ".#",
-                NumberFormatter.with().rounding(Rounder.maxFraction(1)),
+                NumberFormatter.with().precision(Precision.maxFraction(1)),
                 ULocale.ENGLISH,
                 "87,650",
                 "8,765",
@@ -838,7 +838,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "Min/Max Fraction",
                 ".0##",
-                NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 3)),
+                NumberFormatter.with().precision(Precision.minMaxFraction(1, 3)),
                 ULocale.ENGLISH,
                 "87,650.0",
                 "8,765.0",
@@ -856,7 +856,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Fixed Significant",
                 "@@@",
-                NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
+                NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
                 ULocale.ENGLISH,
                 -98,
                 "-98.0");
@@ -864,7 +864,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Fixed Significant Rounding",
                 "@@@",
-                NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
+                NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
                 ULocale.ENGLISH,
                 -98.7654321,
                 "-98.8");
@@ -872,7 +872,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Fixed Significant Zero",
                 "@@@",
-                NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
+                NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
                 ULocale.ENGLISH,
                 0,
                 "0.00");
@@ -880,7 +880,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Min Significant",
                 "@@+",
-                NumberFormatter.with().rounding(Rounder.minDigits(2)),
+                NumberFormatter.with().precision(Precision.minSignificantDigits(2)),
                 ULocale.ENGLISH,
                 -9,
                 "-9.0");
@@ -888,7 +888,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Max Significant",
                 "@###",
-                NumberFormatter.with().rounding(Rounder.maxDigits(4)),
+                NumberFormatter.with().precision(Precision.maxSignificantDigits(4)),
                 ULocale.ENGLISH,
                 98.7654321,
                 "98.77");
@@ -896,7 +896,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Min/Max Significant",
                 "@@@#",
-                NumberFormatter.with().rounding(Rounder.minMaxDigits(3, 4)),
+                NumberFormatter.with().precision(Precision.minMaxSignificantDigits(3, 4)),
                 ULocale.ENGLISH,
                 9.99999,
                 "10.0");
@@ -904,7 +904,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Fixed Significant on zero with zero integer width",
                 "@ integer-width/+",
-                NumberFormatter.with().rounding(Rounder.fixedDigits(1)).integerWidth(IntegerWidth.zeroFillTo(0)),
+                NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(0)),
                 ULocale.ENGLISH,
                 0,
                 "0");
@@ -912,7 +912,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Fixed Significant on zero with lots of integer width",
                 "@ integer-width/+000",
-                NumberFormatter.with().rounding(Rounder.fixedDigits(1)).integerWidth(IntegerWidth.zeroFillTo(3)),
+                NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(3)),
                 ULocale.ENGLISH,
                 0,
                 "000");
@@ -923,7 +923,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "Basic Significant", // for comparison
                 "@#",
-                NumberFormatter.with().rounding(Rounder.maxDigits(2)),
+                NumberFormatter.with().precision(Precision.maxSignificantDigits(2)),
                 ULocale.ENGLISH,
                 "88,000",
                 "8,800",
@@ -938,7 +938,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "FracSig minMaxFrac minSig",
                 ".0#/@@@+",
-                NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 2).withMinDigits(3)),
+                NumberFormatter.with().precision(Precision.minMaxFraction(1, 2).withMinDigits(3)),
                 ULocale.ENGLISH,
                 "87,650.0",
                 "8,765.0",
@@ -953,7 +953,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "FracSig minMaxFrac maxSig A",
                 ".0##/@#",
-                NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 3).withMaxDigits(2)),
+                NumberFormatter.with().precision(Precision.minMaxFraction(1, 3).withMaxDigits(2)),
                 ULocale.ENGLISH,
                 "88,000.0", // maxSig beats maxFrac
                 "8,800.0", // maxSig beats maxFrac
@@ -968,7 +968,7 @@ public class NumberFormatterApiTest {
         assertFormatDescending(
                 "FracSig minMaxFrac maxSig B",
                 ".00/@#",
-                NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMaxDigits(2)),
+                NumberFormatter.with().precision(Precision.fixedFraction(2).withMaxDigits(2)),
                 ULocale.ENGLISH,
                 "88,000.00", // maxSig beats maxFrac
                 "8,800.00", // maxSig beats maxFrac
@@ -983,7 +983,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "FracSig with trailing zeros A",
                 ".00/@@@+",
-                NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMinDigits(3)),
+                NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)),
                 ULocale.ENGLISH,
                 0.1,
                 "0.10");
@@ -991,7 +991,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "FracSig with trailing zeros B",
                 ".00/@@@+",
-                NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMinDigits(3)),
+                NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)),
                 ULocale.ENGLISH,
                 0.0999999,
                 "0.10");
@@ -1001,8 +1001,8 @@ public class NumberFormatterApiTest {
     public void roundingOther() {
         assertFormatDescending(
                 "Rounding None",
-                "round-unlimited",
-                NumberFormatter.with().rounding(Rounder.unlimited()),
+                "precision-unlimited",
+                NumberFormatter.with().precision(Precision.unlimited()),
                 ULocale.ENGLISH,
                 "87,650",
                 "8,765",
@@ -1016,8 +1016,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Increment",
-                "round-increment/0.5",
-                NumberFormatter.with().rounding(Rounder.increment(BigDecimal.valueOf(0.5))),
+                "precision-increment/0.5",
+                NumberFormatter.with().precision(Precision.increment(BigDecimal.valueOf(0.5))),
                 ULocale.ENGLISH,
                 "87,650.0",
                 "8,765.0",
@@ -1031,8 +1031,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Increment with Min Fraction",
-                "round-increment/0.50",
-                NumberFormatter.with().rounding(Rounder.increment(new BigDecimal("0.50"))),
+                "precision-increment/0.50",
+                NumberFormatter.with().precision(Precision.increment(new BigDecimal("0.50"))),
                 ULocale.ENGLISH,
                 "87,650.00",
                 "8,765.00",
@@ -1046,8 +1046,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Currency Standard",
-                "currency/CZK round-currency-standard",
-                NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.STANDARD)).unit(CZK),
+                "currency/CZK precision-currency-standard",
+                NumberFormatter.with().precision(Precision.currency(CurrencyUsage.STANDARD)).unit(CZK),
                 ULocale.ENGLISH,
                 "CZK 87,650.00",
                 "CZK 8,765.00",
@@ -1061,8 +1061,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Currency Cash",
-                "currency/CZK round-currency-cash",
-                NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH)).unit(CZK),
+                "currency/CZK precision-currency-cash",
+                NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CZK),
                 ULocale.ENGLISH,
                 "CZK 87,650",
                 "CZK 8,765",
@@ -1076,8 +1076,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Currency Cash with Nickel Rounding",
-                "currency/CAD round-currency-cash",
-                NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH)).unit(CAD),
+                "currency/CAD precision-currency-cash",
+                NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CAD),
                 ULocale.ENGLISH,
                 "CA$87,650.00",
                 "CA$8,765.00",
@@ -1091,8 +1091,8 @@ public class NumberFormatterApiTest {
 
         assertFormatDescending(
                 "Currency not in top-level fluent chain",
-                "round-integer", // calling .withCurrency() applies currency rounding rules immediately
-                NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH).withCurrency(CZK)),
+                "precision-integer", // calling .withCurrency() applies currency rounding rules immediately
+                NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH).withCurrency(CZK)),
                 ULocale.ENGLISH,
                 "87,650",
                 "8,765",
@@ -1107,8 +1107,8 @@ public class NumberFormatterApiTest {
         // NOTE: Other tests cover the behavior of the other rounding modes.
         assertFormatDescending(
                 "Rounding Mode CEILING",
-                "round-integer/ceiling",
-                NumberFormatter.with().rounding(Rounder.integer().withMode(RoundingMode.CEILING)),
+                "precision-integer rounding-mode-ceiling",
+                NumberFormatter.with().precision(Precision.integer()).roundingMode(RoundingMode.CEILING),
                 ULocale.ENGLISH,
                 "87,650",
                 "8,765",
@@ -2040,7 +2040,7 @@ public class NumberFormatterApiTest {
         // The number needs to have exactly 40 digits, which is the size of the default buffer.
         // (issue discovered by the address sanitizer in C++)
         assertEquals("0.009876543210987654321098765432109876543211",
-                formatter.rounding(Rounder.unlimited())
+                formatter.precision(Precision.unlimited())
                         .format(new BigDecimal("0.009876543210987654321098765432109876543211"))
                         .toString());
     }
@@ -2124,8 +2124,8 @@ public class NumberFormatterApiTest {
 
         assertFormatSingle(
                 "Plural 1",
-                "currency/USD round-integer unit-width-full-name",
-                NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).rounding(Rounder.fixedFraction(0)),
+                "currency/USD precision-integer unit-width-full-name",
+                NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(0)),
                 ULocale.ENGLISH,
                 1,
                 "1 US dollar");
@@ -2133,7 +2133,7 @@ public class NumberFormatterApiTest {
         assertFormatSingle(
                 "Plural 1.00",
                 "currency/USD .00 unit-width-full-name",
-                NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).rounding(Rounder.fixedFraction(2)),
+                NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(2)),
                 ULocale.ENGLISH,
                 1,
                 "1.00 US dollars");
@@ -2141,20 +2141,20 @@ public class NumberFormatterApiTest {
 
     @Test
     public void validRanges() throws NoSuchMethodException, IllegalAccessException {
-        Method[] methodsWithOneArgument = new Method[] { Rounder.class.getDeclaredMethod("fixedFraction", Integer.TYPE),
-                Rounder.class.getDeclaredMethod("minFraction", Integer.TYPE),
-                Rounder.class.getDeclaredMethod("maxFraction", Integer.TYPE),
-                Rounder.class.getDeclaredMethod("fixedDigits", Integer.TYPE),
-                Rounder.class.getDeclaredMethod("minDigits", Integer.TYPE),
-                Rounder.class.getDeclaredMethod("maxDigits", Integer.TYPE),
-                FractionRounder.class.getDeclaredMethod("withMinDigits", Integer.TYPE),
-                FractionRounder.class.getDeclaredMethod("withMaxDigits", Integer.TYPE),
+        Method[] methodsWithOneArgument = new Method[] { Precision.class.getDeclaredMethod("fixedFraction", Integer.TYPE),
+                Precision.class.getDeclaredMethod("minFraction", Integer.TYPE),
+                Precision.class.getDeclaredMethod("maxFraction", Integer.TYPE),
+                Precision.class.getDeclaredMethod("fixedDigits", Integer.TYPE),
+                Precision.class.getDeclaredMethod("minDigits", Integer.TYPE),
+                Precision.class.getDeclaredMethod("maxDigits", Integer.TYPE),
+                FractionPrecision.class.getDeclaredMethod("withMinDigits", Integer.TYPE),
+                FractionPrecision.class.getDeclaredMethod("withMaxDigits", Integer.TYPE),
                 ScientificNotation.class.getDeclaredMethod("withMinExponentDigits", Integer.TYPE),
                 IntegerWidth.class.getDeclaredMethod("zeroFillTo", Integer.TYPE),
                 IntegerWidth.class.getDeclaredMethod("truncateAt", Integer.TYPE), };
         Method[] methodsWithTwoArguments = new Method[] {
-                Rounder.class.getDeclaredMethod("minMaxFraction", Integer.TYPE, Integer.TYPE),
-                Rounder.class.getDeclaredMethod("minMaxDigits", Integer.TYPE, Integer.TYPE), };
+                Precision.class.getDeclaredMethod("minMaxFraction", Integer.TYPE, Integer.TYPE),
+                Precision.class.getDeclaredMethod("minMaxDigits", Integer.TYPE, Integer.TYPE), };
 
         final int EXPECTED_MAX_INT_FRAC_SIG = 999;
         final String expectedSubstring0 = "between 0 and 999 (inclusive)";
@@ -2182,8 +2182,8 @@ public class NumberFormatterApiTest {
 
         // Some of the methods require an object to be called upon.
         Map<String, Object> targets = new HashMap<String, Object>();
-        targets.put("withMinDigits", Rounder.integer());
-        targets.put("withMaxDigits", Rounder.integer());
+        targets.put("withMinDigits", Precision.integer());
+        targets.put("withMaxDigits", Precision.integer());
         targets.put("withMinExponentDigits", Notation.scientific());
         targets.put("truncateAt", IntegerWidth.zeroFillTo(0));
 
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
index 6e854dfbf2c..335b1b23b33 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
@@ -11,7 +11,6 @@ import java.math.RoundingMode;
 import org.junit.Test;
 
 import com.ibm.icu.number.NumberFormatter;
-import com.ibm.icu.number.Rounder;
 import com.ibm.icu.number.SkeletonSyntaxException;
 import com.ibm.icu.util.ULocale;
 
@@ -26,8 +25,8 @@ public class NumberSkeletonTest {
         // This tests only if the tokens are valid, not their behavior.
         // Most of these are from the design doc.
         String[] cases = {
-                "round-integer",
-                "round-unlimited",
+                "precision-integer",
+                "precision-unlimited",
                 "@@@##",
                 "@@+",
                 ".000##",
@@ -37,11 +36,11 @@ public class NumberSkeletonTest {
                 ".######",
                 ".00/@@+",
                 ".00/@##",
-                "round-increment/3.14",
-                "round-currency-standard",
-                "round-integer/half-up",
-                ".00#/ceiling",
-                ".00/@@+/floor",
+                "precision-increment/3.14",
+                "precision-currency-standard",
+                "precision-integer rounding-mode-half-up",
+                ".00# rounding-mode-ceiling",
+                ".00/@@+ rounding-mode-floor",
                 "scientific",
                 "scientific/+ee",
                 "scientific/sign-always",
@@ -91,9 +90,9 @@ public class NumberSkeletonTest {
                 "latin",
                 "numbering-system/arab",
                 "numbering-system/latn",
-                "round-integer/@##",
-                "round-integer/ceiling",
-                "round-currency-cash/ceiling" };
+                "precision-integer/@##",
+                "precision-integer rounding-mode-ceiling",
+                "precision-currency-cash rounding-mode-ceiling" };
 
         for (String cas : cases) {
             try {
@@ -121,12 +120,11 @@ public class NumberSkeletonTest {
                 ".00/@@#",
                 ".00/@@#+",
                 ".00/floor/@@+", // wrong order
-                "round-increment/français", // non-invariant characters for C++
-                "round-currency-cash/XXX",
+                "precision-increment/français", // non-invariant characters for C++
                 "scientific/ee",
-                "round-increment/xxx",
-                "round-increment/NaN",
-                "round-increment/0.1.2",
+                "precision-increment/xxx",
+                "precision-increment/NaN",
+                "precision-increment/0.1.2",
                 "scale/xxx",
                 "scale/NaN",
                 "scale/0.1.2",
@@ -174,10 +172,10 @@ public class NumberSkeletonTest {
     public void unexpectedTokens() {
         String[] cases = {
                 "group-thousands/foo",
-                "round-integer//ceiling group-off",
-                "round-integer//ceiling  group-off",
-                "round-integer/ group-off",
-                "round-integer// group-off" };
+                "precision-integer//@## group-off",
+                "precision-integer//@##  group-off",
+                "precision-integer/ group-off",
+                "precision-integer// group-off" };
 
         for (String cas : cases) {
             try {
@@ -192,10 +190,10 @@ public class NumberSkeletonTest {
     @Test
     public void duplicateValues() {
         String[] cases = {
-                "round-integer round-integer",
-                "round-integer .00+",
-                "round-integer round-unlimited",
-                "round-integer @@@",
+                "precision-integer precision-integer",
+                "precision-integer .00+",
+                "precision-integer precision-unlimited",
+                "precision-integer @@@",
                 "scientific engineering",
                 "engineering compact-long",
                 "sign-auto sign-always" };
@@ -213,14 +211,14 @@ public class NumberSkeletonTest {
     @Test
     public void stemsRequiringOption() {
         String[] stems = {
-                "round-increment",
+                "precision-increment",
                 "measure-unit",
                 "per-unit",
                 "currency",
                 "integer-width",
                 "numbering-system",
                 "scale" };
-        String[] suffixes = { "", "/ceiling", " scientific", "/ceiling scientific" };
+        String[] suffixes = { "", "/@##", " scientific", "/@## scientific" };
 
         for (String stem : stems) {
             for (String suffix : suffixes) {
@@ -255,10 +253,10 @@ public class NumberSkeletonTest {
     @Test
     public void flexibleSeparators() {
         String[][] cases = {
-                { "round-integer group-off", "5142" },
-                { "round-integer  group-off", "5142" },
-                { "round-integer/ceiling group-off", "5143" },
-                { "round-integer/ceiling  group-off", "5143" }, };
+                { "precision-integer group-off", "5142" },
+                { "precision-integer  group-off", "5142" },
+                { "precision-integer/@## group-off", "5140" },
+                { "precision-integer/@##  group-off", "5140" }, };
 
         for (String[] cas : cases) {
             String skeleton = cas[0];
@@ -276,8 +274,7 @@ public class NumberSkeletonTest {
                 // This rounding mode is not printed in the skeleton since it is the default
                 continue;
             }
-            String skeleton = NumberFormatter.with().rounding(Rounder.integer().withMode(mode))
-                    .toSkeleton();
+            String skeleton = NumberFormatter.with().roundingMode(mode).toSkeleton();
             String modeString = mode.toString().toLowerCase().replace('_', '-');
             assertEquals(mode.toString(), modeString, skeleton.substring(14));
         }