ICU-21357 Support IEC Binary Prefixes in MeasureUnit

See #1474
diff --git a/icu4c/source/i18n/measunit_extra.cpp b/icu4c/source/i18n/measunit_extra.cpp
index 9c6d4bd..9556ae9 100644
--- a/icu4c/source/i18n/measunit_extra.cpp
+++ b/icu4c/source/i18n/measunit_extra.cpp
@@ -41,12 +41,20 @@
 // TODO: Propose a new error code for this?
 constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
 
-// Trie value offset for SI Prefixes. This is big enough to ensure we only
+// Trie value offset for SI or binary prefixes. This is big enough to ensure we only
 // insert positive integers into the trie.
-constexpr int32_t kSIPrefixOffset = 64;
+constexpr int32_t kPrefixOffset = 64;
+static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0,
+              "kPrefixOffset is too small for minimum UMeasurePrefix value");
+static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_SI > 0,
+              "kPrefixOffset is too small for minimum UMeasurePrefix value");
 
 // Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
 constexpr int32_t kCompoundPartOffset = 128;
+static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_BIN,
+              "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
+static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_SI,
+              "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
 
 enum CompoundPart {
     // Represents "-per-"
@@ -90,30 +98,40 @@
 // "fluid-ounce-imperial".
 constexpr int32_t kSimpleUnitOffset = 512;
 
-const struct SIPrefixStrings {
+const struct UnitPrefixStrings {
     const char* const string;
-    UMeasureSIPrefix value;
-} gSIPrefixStrings[] = {
-    { "yotta", UMEASURE_SI_PREFIX_YOTTA },
-    { "zetta", UMEASURE_SI_PREFIX_ZETTA },
-    { "exa", UMEASURE_SI_PREFIX_EXA },
-    { "peta", UMEASURE_SI_PREFIX_PETA },
-    { "tera", UMEASURE_SI_PREFIX_TERA },
-    { "giga", UMEASURE_SI_PREFIX_GIGA },
-    { "mega", UMEASURE_SI_PREFIX_MEGA },
-    { "kilo", UMEASURE_SI_PREFIX_KILO },
-    { "hecto", UMEASURE_SI_PREFIX_HECTO },
-    { "deka", UMEASURE_SI_PREFIX_DEKA },
-    { "deci", UMEASURE_SI_PREFIX_DECI },
-    { "centi", UMEASURE_SI_PREFIX_CENTI },
-    { "milli", UMEASURE_SI_PREFIX_MILLI },
-    { "micro", UMEASURE_SI_PREFIX_MICRO },
-    { "nano", UMEASURE_SI_PREFIX_NANO },
-    { "pico", UMEASURE_SI_PREFIX_PICO },
-    { "femto", UMEASURE_SI_PREFIX_FEMTO },
-    { "atto", UMEASURE_SI_PREFIX_ATTO },
-    { "zepto", UMEASURE_SI_PREFIX_ZEPTO },
-    { "yocto", UMEASURE_SI_PREFIX_YOCTO },
+    UMeasurePrefix value;
+} gUnitPrefixStrings[] = {
+    // SI prefixes
+    { "yotta", UMEASURE_PREFIX_YOTTA },
+    { "zetta", UMEASURE_PREFIX_ZETTA },
+    { "exa", UMEASURE_PREFIX_EXA },
+    { "peta", UMEASURE_PREFIX_PETA },
+    { "tera", UMEASURE_PREFIX_TERA },
+    { "giga", UMEASURE_PREFIX_GIGA },
+    { "mega", UMEASURE_PREFIX_MEGA },
+    { "kilo", UMEASURE_PREFIX_KILO },
+    { "hecto", UMEASURE_PREFIX_HECTO },
+    { "deka", UMEASURE_PREFIX_DEKA },
+    { "deci", UMEASURE_PREFIX_DECI },
+    { "centi", UMEASURE_PREFIX_CENTI },
+    { "milli", UMEASURE_PREFIX_MILLI },
+    { "micro", UMEASURE_PREFIX_MICRO },
+    { "nano", UMEASURE_PREFIX_NANO },
+    { "pico", UMEASURE_PREFIX_PICO },
+    { "femto", UMEASURE_PREFIX_FEMTO },
+    { "atto", UMEASURE_PREFIX_ATTO },
+    { "zepto", UMEASURE_PREFIX_ZEPTO },
+    { "yocto", UMEASURE_PREFIX_YOCTO },
+    // Binary prefixes
+    { "yobi", UMEASURE_PREFIX_YOBI },
+    { "zebi", UMEASURE_PREFIX_ZEBI },
+    { "exbi", UMEASURE_PREFIX_EXBI },
+    { "pebi", UMEASURE_PREFIX_PEBI },
+    { "tebi", UMEASURE_PREFIX_TEBI },
+    { "gibi", UMEASURE_PREFIX_GIBI },
+    { "mebi", UMEASURE_PREFIX_MEBI },
+    { "kibi", UMEASURE_PREFIX_KIBI },
 };
 
 /**
@@ -221,9 +239,9 @@
     BytesTrieBuilder b(status);
     if (U_FAILURE(status)) { return; }
 
-    // Add SI prefixes
-    for (const auto& siPrefixInfo : gSIPrefixStrings) {
-        b.add(siPrefixInfo.string, siPrefixInfo.value + kSIPrefixOffset, status);
+    // Add SI and binary prefixes
+    for (const auto& unitPrefixInfo : gUnitPrefixStrings) {
+        b.add(unitPrefixInfo.string, unitPrefixInfo.value + kPrefixOffset, status);
     }
     if (U_FAILURE(status)) { return; }
 
@@ -295,7 +313,7 @@
 
     enum Type {
         TYPE_UNDEFINED,
-        TYPE_SI_PREFIX,
+        TYPE_PREFIX,
         // Token type for "-per-", "-", and "-and-".
         TYPE_COMPOUND_PART,
         // Token type for "per-".
@@ -309,7 +327,7 @@
     Type getType() const {
         U_ASSERT(fMatch > 0);
         if (fMatch < kCompoundPartOffset) {
-            return TYPE_SI_PREFIX;
+            return TYPE_PREFIX;
         }
         if (fMatch < kInitialCompoundPartOffset) {
             return TYPE_COMPOUND_PART;
@@ -323,9 +341,9 @@
         return TYPE_SIMPLE_UNIT;
     }
 
-    UMeasureSIPrefix getSIPrefix() const {
-        U_ASSERT(getType() == TYPE_SI_PREFIX);
-        return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
+    UMeasurePrefix getUnitPrefix() const {
+        U_ASSERT(getType() == TYPE_PREFIX);
+        return static_cast<UMeasurePrefix>(fMatch - kPrefixOffset);
     }
 
     // Valid only for tokens with type TYPE_COMPOUND_PART.
@@ -511,9 +529,9 @@
         }
 
         // state:
-        // 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
+        // 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit)
         // 1 = power token seen (will not accept another power token)
-        // 2 = SI prefix token seen (will not accept a power or SI prefix token)
+        // 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token)
         int32_t state = 0;
 
         bool atStart = fIndex == 0;
@@ -589,12 +607,12 @@
                     state = 1;
                     break;
 
-                case Token::TYPE_SI_PREFIX:
+                case Token::TYPE_PREFIX:
                     if (state > 1) {
                         status = kUnitIdentifierSyntaxError;
                         return result;
                     }
-                    result.siPrefix = token.getSIPrefix();
+                    result.unitPrefix = token.getUnitPrefix();
                     state = 2;
                     break;
 
@@ -622,6 +640,7 @@
     }
 };
 
+// Sorting function wrapping SingleUnitImpl::compareTo for use with uprv_sortArray.
 int32_t U_CALLCONV
 compareSingleUnits(const void* /*context*/, const void* left, const void* right) {
     auto realLeft = static_cast<const SingleUnitImpl* const*>(left);
@@ -631,7 +650,29 @@
 
 } // namespace
 
+U_CAPI int32_t U_EXPORT2
+umeas_getPrefixPower(UMeasurePrefix unitPrefix) {
+    if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
+        unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
+        return unitPrefix - UMEASURE_PREFIX_INTERNAL_ONE_BIN;
+    }
+    U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&
+             unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI);
+    return unitPrefix - UMEASURE_PREFIX_ONE;
+}
 
+U_CAPI int32_t U_EXPORT2
+umeas_getPrefixBase(UMeasurePrefix unitPrefix) {
+    if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
+        unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
+        return 1024;
+    }
+    U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&
+             unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI);
+    return 10;
+}
+
+// In ICU4J, this is MeasureUnit.getSingleUnitImpl().
 SingleUnitImpl SingleUnitImpl::forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status) {
     MeasureUnitImpl temp;
     const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(measureUnit, temp, status);
@@ -682,13 +723,21 @@
         return;
     }
 
-    if (this->siPrefix != UMEASURE_SI_PREFIX_ONE) {
-        for (const auto &siPrefixInfo : gSIPrefixStrings) {
-            if (siPrefixInfo.value == this->siPrefix) {
-                result.append(siPrefixInfo.string, status);
+    if (this->unitPrefix != UMEASURE_PREFIX_ONE) {
+        bool found = false;
+        for (const auto &unitPrefixInfo : gUnitPrefixStrings) {
+            // TODO: consider using binary search? If we do this, add a unit
+            // test to ensure gUnitPrefixStrings is sorted?
+            if (unitPrefixInfo.value == this->unitPrefix) {
+                result.append(unitPrefixInfo.string, status);
+                found = true;
                 break;
             }
         }
+        if (!found) {
+            status = U_UNSUPPORTED_ERROR;
+            return;
+        }
     }
 
     result.append(StringPiece(this->getSimpleUnitID()), status);
@@ -868,13 +917,13 @@
     return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity;
 }
 
-UMeasureSIPrefix MeasureUnit::getSIPrefix(UErrorCode& status) const {
-    return SingleUnitImpl::forMeasureUnit(*this, status).siPrefix;
+UMeasurePrefix MeasureUnit::getPrefix(UErrorCode& status) const {
+    return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix;
 }
 
-MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& status) const {
+MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix, UErrorCode& status) const {
     SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
-    singleUnit.siPrefix = prefix;
+    singleUnit.unitPrefix = prefix;
     return singleUnit.build(status);
 }
 
diff --git a/icu4c/source/i18n/measunit_impl.h b/icu4c/source/i18n/measunit_impl.h
index 137d0ac..bb8575c 100644
--- a/icu4c/source/i18n/measunit_impl.h
+++ b/icu4c/source/i18n/measunit_impl.h
@@ -42,7 +42,7 @@
 };
 
 /**
- * A struct representing a single unit (optional SI prefix and dimensionality).
+ * A struct representing a single unit (optional SI or binary prefix, and dimensionality).
  */
 struct U_I18N_API SingleUnitImpl : public UMemory {
     /**
@@ -96,10 +96,12 @@
         if (index > other.index) {
             return 1;
         }
-        if (siPrefix < other.siPrefix) {
+        // TODO(icu-units#70): revisit when fixing normalization. For now we're
+        // sorting binary prefixes before SI prefixes, as per enum values order.
+        if (unitPrefix < other.unitPrefix) {
             return -1;
         }
-        if (siPrefix > other.siPrefix) {
+        if (unitPrefix > other.unitPrefix) {
             return 1;
         }
         return 0;
@@ -108,8 +110,8 @@
     /**
      * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
      *
-     * Units with the same base unit and SI prefix should match, except that they must also have
-     * the same dimensionality sign, such that we don't merge numerator and denominator.
+     * Units with the same base unit and SI or binary prefix should match, except that they must also
+     * have the same dimensionality sign, such that we don't merge numerator and denominator.
      */
     bool isCompatibleWith(const SingleUnitImpl& other) const {
         return (compareTo(other) == 0);
@@ -134,11 +136,11 @@
     int32_t index = -1;
 
     /**
-     * SI prefix.
+     * SI or binary prefix.
      *
      * This is ignored for the dimensionless unit.
      */
-    UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
+    UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE;
 
     /**
      * Dimensionality.
diff --git a/icu4c/source/i18n/unicode/measunit.h b/icu4c/source/i18n/unicode/measunit.h
index 6761f84..f8168c2 100644
--- a/icu4c/source/i18n/unicode/measunit.h
+++ b/icu4c/source/i18n/unicode/measunit.h
@@ -36,15 +36,15 @@
 /**
  * Enumeration for unit complexity. There are three levels:
  *
- * - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
- *           square-kilometer, kilojoule, per-second.
+ * - SINGLE: A single unit, optionally with a power and/or SI or binary prefix.
+ *           Examples: hectare, square-kilometer, kilojoule, per-second, mebibyte.
  * - COMPOUND: A unit composed of the product of multiple single units. Examples:
  *             meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
  * - MIXED: A unit composed of the sum of multiple single units. Examples: foot+inch,
  *          hour+minute+second, degree+arcminute+arcsecond.
  *
  * The complexity determines which operations are available. For example, you cannot set the power
- * or SI prefix of a compound unit.
+ * or prefix of a compound unit.
  *
  * @draft ICU 67
  */
@@ -72,159 +72,276 @@
 };
 
 /**
- * Enumeration for SI prefixes, such as "kilo".
+ * Enumeration for SI and binary prefixes, e.g. "kilo-", "nano-", "mebi-".
  *
- * @draft ICU 67
+ * Enum values should be treated as opaque: use umeas_getPrefixPower() and
+ * umeas_getPrefixBase() to find their corresponding values.
+ *
+ * @draft ICU 69
+ * @see umeas_getPrefixBase
+ * @see umeas_getPrefixPower
  */
-typedef enum UMeasureSIPrefix {
+typedef enum UMeasurePrefix {
+    /**
+     * The absence of an SI or binary prefix.
+     *
+     * The integer representation of this enum value is an arbitrary
+     * implementation detail and should not be relied upon: use
+     * umeas_getPrefixPower() to obtain meaningful values.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_ONE = 30 + 0,
 
     /**
      * SI prefix: yotta, 10^24.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_YOTTA = 24,
+    UMEASURE_PREFIX_YOTTA = UMEASURE_PREFIX_ONE + 24,
+
+    /**
+     * ICU use only.
+     * Used to determine the set of base-10 SI prefixes.
+     * @internal
+     */
+    UMEASURE_PREFIX_INTERNAL_MAX_SI = UMEASURE_PREFIX_YOTTA,
 
     /**
      * SI prefix: zetta, 10^21.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_ZETTA = 21,
+    UMEASURE_PREFIX_ZETTA = UMEASURE_PREFIX_ONE + 21,
 
     /**
      * SI prefix: exa, 10^18.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_EXA = 18,
+    UMEASURE_PREFIX_EXA = UMEASURE_PREFIX_ONE + 18,
 
     /**
      * SI prefix: peta, 10^15.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_PETA = 15,
+    UMEASURE_PREFIX_PETA = UMEASURE_PREFIX_ONE + 15,
 
     /**
      * SI prefix: tera, 10^12.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_TERA = 12,
+    UMEASURE_PREFIX_TERA = UMEASURE_PREFIX_ONE + 12,
 
     /**
      * SI prefix: giga, 10^9.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_GIGA = 9,
+    UMEASURE_PREFIX_GIGA = UMEASURE_PREFIX_ONE + 9,
 
     /**
      * SI prefix: mega, 10^6.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_MEGA = 6,
+    UMEASURE_PREFIX_MEGA = UMEASURE_PREFIX_ONE + 6,
 
     /**
      * SI prefix: kilo, 10^3.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_KILO = 3,
+    UMEASURE_PREFIX_KILO = UMEASURE_PREFIX_ONE + 3,
 
     /**
      * SI prefix: hecto, 10^2.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_HECTO = 2,
+    UMEASURE_PREFIX_HECTO = UMEASURE_PREFIX_ONE + 2,
 
     /**
      * SI prefix: deka, 10^1.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_DEKA = 1,
-
-    /**
-     * The absence of an SI prefix.
-     *
-     * @draft ICU 67
-     */
-    UMEASURE_SI_PREFIX_ONE = 0,
+    UMEASURE_PREFIX_DEKA = UMEASURE_PREFIX_ONE + 1,
 
     /**
      * SI prefix: deci, 10^-1.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_DECI = -1,
+    UMEASURE_PREFIX_DECI = UMEASURE_PREFIX_ONE + -1,
 
     /**
      * SI prefix: centi, 10^-2.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_CENTI = -2,
+    UMEASURE_PREFIX_CENTI = UMEASURE_PREFIX_ONE + -2,
 
     /**
      * SI prefix: milli, 10^-3.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_MILLI = -3,
+    UMEASURE_PREFIX_MILLI = UMEASURE_PREFIX_ONE + -3,
 
     /**
      * SI prefix: micro, 10^-6.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_MICRO = -6,
+    UMEASURE_PREFIX_MICRO = UMEASURE_PREFIX_ONE + -6,
 
     /**
      * SI prefix: nano, 10^-9.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_NANO = -9,
+    UMEASURE_PREFIX_NANO = UMEASURE_PREFIX_ONE + -9,
 
     /**
      * SI prefix: pico, 10^-12.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_PICO = -12,
+    UMEASURE_PREFIX_PICO = UMEASURE_PREFIX_ONE + -12,
 
     /**
      * SI prefix: femto, 10^-15.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_FEMTO = -15,
+    UMEASURE_PREFIX_FEMTO = UMEASURE_PREFIX_ONE + -15,
 
     /**
      * SI prefix: atto, 10^-18.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_ATTO = -18,
+    UMEASURE_PREFIX_ATTO = UMEASURE_PREFIX_ONE + -18,
 
     /**
      * SI prefix: zepto, 10^-21.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_ZEPTO = -21,
+    UMEASURE_PREFIX_ZEPTO = UMEASURE_PREFIX_ONE + -21,
 
     /**
      * SI prefix: yocto, 10^-24.
      *
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    UMEASURE_SI_PREFIX_YOCTO = -24
-} UMeasureSIPrefix;
+    UMEASURE_PREFIX_YOCTO = UMEASURE_PREFIX_ONE + -24,
+
+    /**
+     * ICU use only.
+     * Used to determine the set of base-10 SI prefixes.
+     * @internal
+     */
+    UMEASURE_PREFIX_INTERNAL_MIN_SI = UMEASURE_PREFIX_YOCTO,
+
+    /**
+     * ICU use only.
+     * Sets the arbitrary offset of the base-1024 binary prefixes' enum values.
+     * @internal
+     */
+    UMEASURE_PREFIX_INTERNAL_ONE_BIN = -60,
+
+    /**
+     * Binary prefix: kibi, 1024^1.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_KIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 1,
+
+    /**
+     * ICU use only.
+     * Used to determine the set of base-1024 binary prefixes.
+     * @internal
+     */
+    UMEASURE_PREFIX_INTERNAL_MIN_BIN = UMEASURE_PREFIX_KIBI,
+
+    /**
+     * Binary prefix: mebi, 1024^2.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_MEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 2,
+
+    /**
+     * Binary prefix: gibi, 1024^3.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_GIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 3,
+
+    /**
+     * Binary prefix: tebi, 1024^4.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_TEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 4,
+
+    /**
+     * Binary prefix: pebi, 1024^5.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_PEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 5,
+
+    /**
+     * Binary prefix: exbi, 1024^6.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_EXBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 6,
+
+    /**
+     * Binary prefix: zebi, 1024^7.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_ZEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 7,
+
+    /**
+     * Binary prefix: yobi, 1024^8.
+     *
+     * @draft ICU 69
+     */
+    UMEASURE_PREFIX_YOBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 8,
+
+    /**
+     * ICU use only.
+     * Used to determine the set of base-1024 binary prefixes.
+     * @internal
+     */
+    UMEASURE_PREFIX_INTERNAL_MAX_BIN = UMEASURE_PREFIX_YOBI,
+} UMeasurePrefix;
+
+/**
+ * Returns the base of the factor associated with the given unit prefix: the
+ * base is 10 for SI prefixes (kilo, micro) and 1024 for binary prefixes (kibi,
+ * mebi).
+ *
+ * @draft ICU 69
+ */
+U_CAPI int32_t U_EXPORT2 umeas_getPrefixBase(UMeasurePrefix unitPrefix);
+
+/**
+ * Returns the exponent of the factor associated with the given unit prefix, for
+ * example 3 for kilo, -6 for micro, 1 for kibi, 2 for mebi, 3 for gibi.
+ *
+ * @draft ICU 69
+ */
+U_CAPI int32_t U_EXPORT2 umeas_getPrefixPower(UMeasurePrefix unitPrefix);
+
 #endif // U_HIDE_DRAFT_API
 
 /**
@@ -352,33 +469,36 @@
     UMeasureUnitComplexity getComplexity(UErrorCode& status) const;
 
     /**
-     * Creates a MeasureUnit which is this SINGLE unit augmented with the specified SI prefix.
-     * For example, UMEASURE_SI_PREFIX_KILO for "kilo".
+     * Creates a MeasureUnit which is this SINGLE unit augmented with the specified prefix.
+     * For example, UMEASURE_PREFIX_KILO for "kilo", or UMEASURE_PREFIX_KIBI for "kibi".
      *
-     * There is sufficient locale data to format all standard SI prefixes.
+     * There is sufficient locale data to format all standard prefixes.
      *
      * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
      * occur. For more information, see UMeasureUnitComplexity.
      *
-     * @param prefix The SI prefix, from UMeasureSIPrefix.
+     * @param prefix The prefix, from UMeasurePrefix.
      * @param status Set if this is not a SINGLE unit or if another error occurs.
      * @return A new SINGLE unit.
-     * @draft ICU 67
+     * @draft ICU 69
      */
-    MeasureUnit withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& status) const;
+    MeasureUnit withPrefix(UMeasurePrefix prefix, UErrorCode& status) const;
 
     /**
-     * Gets the current SI prefix of this SINGLE unit. For example, if the unit has the SI prefix
-     * "kilo", then UMEASURE_SI_PREFIX_KILO is returned.
+     * Returns the current SI or binary prefix of this SINGLE unit. For example,
+     * if the unit has the prefix "kilo", then UMEASURE_PREFIX_KILO is
+     * returned.
      *
      * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
      * occur. For more information, see UMeasureUnitComplexity.
      *
      * @param status Set if this is not a SINGLE unit or if another error occurs.
-     * @return The SI prefix of this SINGLE unit, from UMeasureSIPrefix.
-     * @draft ICU 67
+     * @return The prefix of this SINGLE unit, from UMeasurePrefix.
+     * @see umeas_getPrefixBase
+     * @see umeas_getPrefixPower
+     * @draft ICU 69
      */
-    UMeasureSIPrefix getSIPrefix(UErrorCode& status) const;
+    UMeasurePrefix getPrefix(UErrorCode& status) const;
 
     /**
      * Creates a MeasureUnit which is this SINGLE unit augmented with the specified dimensionality
diff --git a/icu4c/source/i18n/units_converter.cpp b/icu4c/source/i18n/units_converter.cpp
index 085789a..9cd8856 100644
--- a/icu4c/source/i18n/units_converter.cpp
+++ b/icu4c/source/i18n/units_converter.cpp
@@ -66,17 +66,19 @@
     }
 }
 
-void U_I18N_API Factor::applySiPrefix(UMeasureSIPrefix siPrefix) {
-    if (siPrefix == UMeasureSIPrefix::UMEASURE_SI_PREFIX_ONE) return; // No need to do anything
-
-    double siApplied = std::pow(10.0, std::abs(siPrefix));
-
-    if (siPrefix < 0) {
-        factorDen *= siApplied;
+void U_I18N_API Factor::applyPrefix(UMeasurePrefix unitPrefix) {
+    if (unitPrefix == UMeasurePrefix::UMEASURE_PREFIX_ONE) {
+        // No need to do anything
         return;
     }
 
-    factorNum *= siApplied;
+    int32_t prefixPower = umeas_getPrefixPower(unitPrefix);
+    double prefixFactor = std::pow((double)umeas_getPrefixBase(unitPrefix), (double)std::abs(prefixPower));
+    if (prefixPower >= 0) {
+        factorNum *= prefixFactor;
+    } else {
+        factorDen *= prefixFactor;
+    }
 }
 
 void U_I18N_API Factor::substituteConstants() {
@@ -213,6 +215,7 @@
 }
 
 // Load Factor of a compound source unit.
+// In ICU4J, this is a pair of ConversionRates.getFactorToBase() functions.
 Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo,
                           UErrorCode &status) {
 
@@ -223,8 +226,10 @@
         Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status);
         if (U_FAILURE(status)) return result;
 
-        // Apply SiPrefix before the power, because the power may be will flip the factor.
-        singleFactor.applySiPrefix(singleUnit.siPrefix);
+        // Prefix before power, because:
+        // - square-kilometer to square-meter: (1000)^2
+        // - square-kilometer to square-foot (approximate): (3.28*1000)^2
+        singleFactor.applyPrefix(singleUnit.unitPrefix);
 
         // Apply the power of the `dimensionality`
         singleFactor.power(singleUnit.dimensionality);
@@ -241,6 +246,8 @@
  *
  * NOTE:
  *  Empty unit means simple unit.
+ *
+ * In ICU4J, this is ConversionRates.checkSimpleUnit().
  */
 UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
     if (U_FAILURE(status)) return false;
@@ -255,7 +262,7 @@
 
     auto singleUnit = *(unit.singleUnits[0]);
 
-    if (singleUnit.dimensionality != 1 || singleUnit.siPrefix != UMEASURE_SI_PREFIX_ONE) {
+    if (singleUnit.dimensionality != 1 || singleUnit.unitPrefix != UMEASURE_PREFIX_ONE) {
         return false;
     }
 
@@ -293,6 +300,7 @@
     conversionRate.factorNum = finalFactor.factorNum;
     conversionRate.factorDen = finalFactor.factorDen;
 
+    // This code corresponds to ICU4J's ConversionRates.getOffset().
     // In case of simple units (such as: celsius or fahrenheit), offsets are considered.
     if (checkSimpleUnit(source, status) && checkSimpleUnit(target, status)) {
         conversionRate.sourceOffset =
@@ -300,6 +308,8 @@
         conversionRate.targetOffset =
             targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum;
     }
+    // TODO(icu-units#127): should we consider failure if there's an offset for
+    // a not-simple-unit? What about kilokelvin / kilocelsius?
 
     conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL;
 }
diff --git a/icu4c/source/i18n/units_converter.h b/icu4c/source/i18n/units_converter.h
index f71628d..b9c1e31 100644
--- a/icu4c/source/i18n/units_converter.h
+++ b/icu4c/source/i18n/units_converter.h
@@ -68,8 +68,8 @@
     // Apply the power to the factor.
     void power(int32_t power);
 
-    // Apply SI prefix to the `Factor`
-    void applySiPrefix(UMeasureSIPrefix siPrefix);
+    // Apply SI or binary prefix to the Factor.
+    void applyPrefix(UMeasurePrefix unitPrefix);
 
     // Does an in-place substition of the "symbolic constants" based on
     // constantExponents (resetting the exponents).
diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp
index c018039..23ac5cc 100644
--- a/icu4c/source/test/intltest/measfmttest.cpp
+++ b/icu4c/source/test/intltest/measfmttest.cpp
@@ -83,6 +83,8 @@
     void TestNumericTimeSomeSpecialFormats();
     void TestIdentifiers();
     void TestInvalidIdentifiers();
+    void TestIdentifierDetails();
+    void TestPrefixes();
     void TestParseToBuiltIn();
     void TestKilogramIdentifier();
     void TestCompoundUnitOperations();
@@ -152,7 +154,7 @@
         int32_t end);
     void verifySingleUnit(
         const MeasureUnit& unit,
-        UMeasureSIPrefix siPrefix,
+        UMeasurePrefix unitPrefix,
         int8_t power,
         const char* identifier);
     void verifyCompoundUnit(
@@ -212,6 +214,8 @@
     TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
     TESTCASE_AUTO(TestIdentifiers);
     TESTCASE_AUTO(TestInvalidIdentifiers);
+    TESTCASE_AUTO(TestIdentifierDetails);
+    TESTCASE_AUTO(TestPrefixes);
     TESTCASE_AUTO(TestParseToBuiltIn);
     TESTCASE_AUTO(TestKilogramIdentifier);
     TESTCASE_AUTO(TestCompoundUnitOperations);
@@ -3664,6 +3668,45 @@
         {"kilogram-per-meter-per-second", "kilogram-per-meter-second"},
 
         // TODO(ICU-21284): Add more test cases once the proper ranking is available.
+
+        // Testing prefixes are parsed and produced correctly (ensures no
+        // collisions in the enum values)
+        {"yoctofoot", "yoctofoot"},
+        {"zeptofoot", "zeptofoot"},
+        {"attofoot", "attofoot"},
+        {"femtofoot", "femtofoot"},
+        {"picofoot", "picofoot"},
+        {"nanofoot", "nanofoot"},
+        {"microfoot", "microfoot"},
+        {"millifoot", "millifoot"},
+        {"centifoot", "centifoot"},
+        {"decifoot", "decifoot"},
+        {"foot", "foot"},
+        {"dekafoot", "dekafoot"},
+        {"hectofoot", "hectofoot"},
+        {"kilofoot", "kilofoot"},
+        {"megafoot", "megafoot"},
+        {"gigafoot", "gigafoot"},
+        {"terafoot", "terafoot"},
+        {"petafoot", "petafoot"},
+        {"exafoot", "exafoot"},
+        {"zettafoot", "zettafoot"},
+        {"yottafoot", "yottafoot"},
+        {"kibibyte", "kibibyte"},
+        {"mebibyte", "mebibyte"},
+        {"gibibyte", "gibibyte"},
+        {"tebibyte", "tebibyte"},
+        {"pebibyte", "pebibyte"},
+        {"exbibyte", "exbibyte"},
+        {"zebibyte", "zebibyte"},
+        {"yobibyte", "yobibyte"},
+
+        // Testing sort order of prefixes.
+        //
+        // TODO(icu-units#70): revisit when fixing normalization. For now we're
+        // just checking some consistency between C&J.
+        {"megafoot-mebifoot-kibifoot-kilofoot", "kibifoot-mebifoot-kilofoot-megafoot"},
+
     };
     for (const auto &cas : cases) {
         status.setScope(cas.id);
@@ -3722,6 +3765,77 @@
     }
 }
 
+void MeasureFormatTest::TestIdentifierDetails() {
+    IcuTestErrorCode status(*this, "TestIdentifierDetails()");
+
+    MeasureUnit joule = MeasureUnit::forIdentifier("joule", status);
+    status.assertSuccess();
+    assertEquals("Initial joule", "joule", joule.getIdentifier());
+
+    static_assert(UMEASURE_PREFIX_INTERNAL_MAX_SI < 99, "Tests assume there is no prefix 99.");
+    static_assert(UMEASURE_PREFIX_INTERNAL_MAX_BIN < 99, "Tests assume there is no prefix 99.");
+    MeasureUnit unit = joule.withPrefix((UMeasurePrefix)99, status);
+    if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
+        errln("Invalid prefix should result in an error.");
+    }
+    assertEquals("Invalid prefix results in no identifier", "", unit.getIdentifier());
+
+    unit = joule.withPrefix(UMEASURE_PREFIX_HECTO, status);
+    status.assertSuccess();
+    assertEquals("foo identifier", "hectojoule", unit.getIdentifier());
+
+    unit = unit.withPrefix(UMEASURE_PREFIX_EXBI, status);
+    status.assertSuccess();
+    assertEquals("foo identifier", "exbijoule", unit.getIdentifier());
+}
+
+void MeasureFormatTest::TestPrefixes() {
+    IcuTestErrorCode status(*this, "TestPrefixes()");
+    const struct TestCase {
+        UMeasurePrefix prefix;
+        int32_t expectedBase;
+        int32_t expectedPower;
+    } cases[] = {
+        {UMEASURE_PREFIX_YOCTO, 10, -24},
+        {UMEASURE_PREFIX_ZEPTO, 10, -21},
+        {UMEASURE_PREFIX_ATTO, 10, -18},
+        {UMEASURE_PREFIX_FEMTO, 10, -15},
+        {UMEASURE_PREFIX_PICO, 10, -12},
+        {UMEASURE_PREFIX_NANO, 10, -9},
+        {UMEASURE_PREFIX_MICRO, 10, -6},
+        {UMEASURE_PREFIX_MILLI, 10, -3},
+        {UMEASURE_PREFIX_CENTI, 10, -2},
+        {UMEASURE_PREFIX_DECI, 10, -1},
+        {UMEASURE_PREFIX_ONE, 10, 0},
+        {UMEASURE_PREFIX_DEKA, 10, 1},
+        {UMEASURE_PREFIX_HECTO, 10, 2},
+        {UMEASURE_PREFIX_KILO, 10, 3},
+        {UMEASURE_PREFIX_MEGA, 10, 6},
+        {UMEASURE_PREFIX_GIGA, 10, 9},
+        {UMEASURE_PREFIX_TERA, 10, 12},
+        {UMEASURE_PREFIX_PETA, 10, 15},
+        {UMEASURE_PREFIX_EXA, 10, 18},
+        {UMEASURE_PREFIX_ZETTA, 10, 21},
+        {UMEASURE_PREFIX_YOTTA, 10, 24},
+        {UMEASURE_PREFIX_KIBI, 1024, 1},
+        {UMEASURE_PREFIX_MEBI, 1024, 2},
+        {UMEASURE_PREFIX_GIBI, 1024, 3},
+        {UMEASURE_PREFIX_TEBI, 1024, 4},
+        {UMEASURE_PREFIX_PEBI, 1024, 5},
+        {UMEASURE_PREFIX_EXBI, 1024, 6},
+        {UMEASURE_PREFIX_ZEBI, 1024, 7},
+        {UMEASURE_PREFIX_YOBI, 1024, 8},
+    };
+
+    for (auto cas : cases) {
+        MeasureUnit m = MeasureUnit::getAmpere().withPrefix(cas.prefix, status);
+        assertEquals("umeas_getPrefixPower()", cas.expectedPower,
+                     umeas_getPrefixPower(m.getPrefix(status)));
+        assertEquals("umeas_getPrefixBase()", cas.expectedBase,
+                     umeas_getPrefixBase(m.getPrefix(status)));
+    }
+}
+
 void MeasureFormatTest::TestParseToBuiltIn() {
     IcuTestErrorCode status(*this, "TestParseToBuiltIn()");
     const struct TestCase {
@@ -3778,12 +3892,12 @@
     assertEquals("nanogram", "", nanogram.getType());
     assertEquals("nanogram", "nanogram", nanogram.getIdentifier());
 
-    assertEquals("prefix of kilogram", UMEASURE_SI_PREFIX_KILO, kilogram.getSIPrefix(status));
-    assertEquals("prefix of gram", UMEASURE_SI_PREFIX_ONE, gram.getSIPrefix(status));
-    assertEquals("prefix of microgram", UMEASURE_SI_PREFIX_MICRO, microgram.getSIPrefix(status));
-    assertEquals("prefix of nanogram", UMEASURE_SI_PREFIX_NANO, nanogram.getSIPrefix(status));
+    assertEquals("prefix of kilogram", UMEASURE_PREFIX_KILO, kilogram.getPrefix(status));
+    assertEquals("prefix of gram", UMEASURE_PREFIX_ONE, gram.getPrefix(status));
+    assertEquals("prefix of microgram", UMEASURE_PREFIX_MICRO, microgram.getPrefix(status));
+    assertEquals("prefix of nanogram", UMEASURE_PREFIX_NANO, nanogram.getPrefix(status));
 
-    MeasureUnit tmp = kilogram.withSIPrefix(UMEASURE_SI_PREFIX_MILLI, status);
+    MeasureUnit tmp = kilogram.withPrefix(UMEASURE_PREFIX_MILLI, status);
     assertEquals(UnicodeString("Kilogram + milli should be milligram, got: ") + tmp.getIdentifier(),
                  MeasureUnit::getMilligram().getIdentifier(), tmp.getIdentifier());
 }
@@ -3795,16 +3909,16 @@
 
     MeasureUnit kilometer = MeasureUnit::getKilometer();
     MeasureUnit cubicMeter = MeasureUnit::getCubicMeter();
-    MeasureUnit meter = kilometer.withSIPrefix(UMEASURE_SI_PREFIX_ONE, status);
-    MeasureUnit centimeter1 = kilometer.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
-    MeasureUnit centimeter2 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
-    MeasureUnit cubicDecimeter = cubicMeter.withSIPrefix(UMEASURE_SI_PREFIX_DECI, status);
+    MeasureUnit meter = kilometer.withPrefix(UMEASURE_PREFIX_ONE, status);
+    MeasureUnit centimeter1 = kilometer.withPrefix(UMEASURE_PREFIX_CENTI, status);
+    MeasureUnit centimeter2 = meter.withPrefix(UMEASURE_PREFIX_CENTI, status);
+    MeasureUnit cubicDecimeter = cubicMeter.withPrefix(UMEASURE_PREFIX_DECI, status);
 
-    verifySingleUnit(kilometer, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
-    verifySingleUnit(meter, UMEASURE_SI_PREFIX_ONE, 1, "meter");
-    verifySingleUnit(centimeter1, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
-    verifySingleUnit(centimeter2, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
-    verifySingleUnit(cubicDecimeter, UMEASURE_SI_PREFIX_DECI, 3, "cubic-decimeter");
+    verifySingleUnit(kilometer, UMEASURE_PREFIX_KILO, 1, "kilometer");
+    verifySingleUnit(meter, UMEASURE_PREFIX_ONE, 1, "meter");
+    verifySingleUnit(centimeter1, UMEASURE_PREFIX_CENTI, 1, "centimeter");
+    verifySingleUnit(centimeter2, UMEASURE_PREFIX_CENTI, 1, "centimeter");
+    verifySingleUnit(cubicDecimeter, UMEASURE_PREFIX_DECI, 3, "cubic-decimeter");
 
     assertTrue("centimeter equality", centimeter1 == centimeter2);
     assertTrue("kilometer inequality", centimeter1 != kilometer);
@@ -3814,10 +3928,10 @@
     MeasureUnit quarticKilometer = kilometer.withDimensionality(4, status);
     MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4, status);
 
-    verifySingleUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
-    verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "per-cubic-centimeter");
-    verifySingleUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "pow4-kilometer");
-    verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
+    verifySingleUnit(squareMeter, UMEASURE_PREFIX_ONE, 2, "square-meter");
+    verifySingleUnit(overCubicCentimeter, UMEASURE_PREFIX_CENTI, -3, "per-cubic-centimeter");
+    verifySingleUnit(quarticKilometer, UMEASURE_PREFIX_KILO, 4, "pow4-kilometer");
+    verifySingleUnit(overQuarticKilometer1, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
 
     assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
 
@@ -3828,26 +3942,26 @@
         .reciprocal(status);
     MeasureUnit overQuarticKilometer4 = meter.withDimensionality(4, status)
         .reciprocal(status)
-        .withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
+        .withPrefix(UMEASURE_PREFIX_KILO, status);
 
-    verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
-    verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
-    verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
+    verifySingleUnit(overQuarticKilometer2, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
+    verifySingleUnit(overQuarticKilometer3, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
+    verifySingleUnit(overQuarticKilometer4, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
 
     assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
     assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
     assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer4);
 
     MeasureUnit kiloSquareSecond = MeasureUnit::getSecond()
-        .withDimensionality(2, status).withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
+        .withDimensionality(2, status).withPrefix(UMEASURE_PREFIX_KILO, status);
     MeasureUnit meterSecond = meter.product(kiloSquareSecond, status);
     MeasureUnit cubicMeterSecond1 = meter.withDimensionality(3, status).product(kiloSquareSecond, status);
-    MeasureUnit centimeterSecond1 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status).product(kiloSquareSecond, status);
+    MeasureUnit centimeterSecond1 = meter.withPrefix(UMEASURE_PREFIX_CENTI, status).product(kiloSquareSecond, status);
     MeasureUnit secondCubicMeter = kiloSquareSecond.product(meter.withDimensionality(3, status), status);
-    MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status), status);
+    MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withPrefix(UMEASURE_PREFIX_CENTI, status), status);
     MeasureUnit secondCentimeterPerKilometer = secondCentimeter.product(kilometer.reciprocal(status), status);
 
-    verifySingleUnit(kiloSquareSecond, UMEASURE_SI_PREFIX_KILO, 2, "square-kilosecond");
+    verifySingleUnit(kiloSquareSecond, UMEASURE_PREFIX_KILO, 2, "square-kilosecond");
     const char* meterSecondSub[] = {"meter", "square-kilosecond"};
     verifyCompoundUnit(meterSecond, "meter-square-kilosecond",
         meterSecondSub, UPRV_LENGTHOF(meterSecondSub));
@@ -3870,20 +3984,20 @@
     assertTrue("reordering equality", cubicMeterSecond1 == secondCubicMeter);
     assertTrue("additional simple units inequality", secondCubicMeter != secondCentimeter);
 
-    // Don't allow get/set power or SI prefix on compound units
+    // Don't allow get/set power or SI or binary prefix on compound units
     status.errIfFailureAndReset();
     meterSecond.getDimensionality(status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
     meterSecond.withDimensionality(3, status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
-    meterSecond.getSIPrefix(status);
+    meterSecond.getPrefix(status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
-    meterSecond.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
+    meterSecond.withPrefix(UMEASURE_PREFIX_CENTI, status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
 
     // Test that StringPiece does not overflow
     MeasureUnit centimeter3 = MeasureUnit::forIdentifier({secondCentimeter.getIdentifier(), 10}, status);
-    verifySingleUnit(centimeter3, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
+    verifySingleUnit(centimeter3, UMEASURE_PREFIX_CENTI, 1, "centimeter");
     assertTrue("string piece equality", centimeter1 == centimeter3);
 
     MeasureUnit footInch = MeasureUnit::forIdentifier("foot-and-inch", status);
@@ -3907,19 +4021,19 @@
     // with others via product:
     MeasureUnit kilometer2 = dimensionless.product(kilometer, status);
     status.errIfFailureAndReset("dimensionless.product(kilometer, status)");
-    verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
+    verifySingleUnit(kilometer2, UMEASURE_PREFIX_KILO, 1, "kilometer");
     assertTrue("kilometer equality", kilometer == kilometer2);
 
     // Test out-of-range powers
     MeasureUnit power15 = MeasureUnit::forIdentifier("pow15-kilometer", status);
-    verifySingleUnit(power15, UMEASURE_SI_PREFIX_KILO, 15, "pow15-kilometer");
+    verifySingleUnit(power15, UMEASURE_PREFIX_KILO, 15, "pow15-kilometer");
     status.errIfFailureAndReset();
     MeasureUnit power16a = MeasureUnit::forIdentifier("pow16-kilometer", status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
     MeasureUnit power16b = power15.product(kilometer, status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
     MeasureUnit powerN15 = MeasureUnit::forIdentifier("per-pow15-kilometer", status);
-    verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "per-pow15-kilometer");
+    verifySingleUnit(powerN15, UMEASURE_PREFIX_KILO, -15, "per-pow15-kilometer");
     status.errIfFailureAndReset();
     MeasureUnit powerN16a = MeasureUnit::forIdentifier("per-pow16-kilometer", status);
     status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
@@ -3945,22 +4059,22 @@
     MeasureUnit mile = MeasureUnit::getMile();
     mile = mile.product(dimensionless, status);
     status.errIfFailureAndReset("mile.product(dimensionless, ...)");
-    verifySingleUnit(mile, UMEASURE_SI_PREFIX_ONE, 1, "mile");
+    verifySingleUnit(mile, UMEASURE_PREFIX_ONE, 1, "mile");
 
-    // dimensionless.getSIPrefix()
-    UMeasureSIPrefix siPrefix = dimensionless.getSIPrefix(status);
-    status.errIfFailureAndReset("dimensionless.getSIPrefix(...)");
-    assertEquals("dimensionless SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
+    // dimensionless.getPrefix()
+    UMeasurePrefix unitPrefix = dimensionless.getPrefix(status);
+    status.errIfFailureAndReset("dimensionless.getPrefix(...)");
+    assertEquals("dimensionless SIPrefix", UMEASURE_PREFIX_ONE, unitPrefix);
 
-    // dimensionless.withSIPrefix()
-    modified = dimensionless.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
-    status.errIfFailureAndReset("dimensionless.withSIPrefix(...)");
+    // dimensionless.withPrefix()
+    modified = dimensionless.withPrefix(UMEASURE_PREFIX_KILO, status);
+    status.errIfFailureAndReset("dimensionless.withPrefix(...)");
     pair = dimensionless.splitToSingleUnits(status);
     count = pair.second;
     assertEquals("no singles in modified", 0, count);
-    siPrefix = modified.getSIPrefix(status);
-    status.errIfFailureAndReset("modified.getSIPrefix(...)");
-    assertEquals("modified SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
+    unitPrefix = modified.getPrefix(status);
+    status.errIfFailureAndReset("modified.getPrefix(...)");
+    assertEquals("modified SIPrefix", UMEASURE_PREFIX_ONE, unitPrefix);
 
     // dimensionless.getComplexity()
     UMeasureUnitComplexity complexity = dimensionless.getComplexity(status);
@@ -4192,15 +4306,15 @@
 
 void MeasureFormatTest::verifySingleUnit(
         const MeasureUnit& unit,
-        UMeasureSIPrefix siPrefix,
+        UMeasurePrefix unitPrefix,
         int8_t power,
         const char* identifier) {
     IcuTestErrorCode status(*this, "verifySingleUnit");
     UnicodeString uid(identifier, -1, US_INV);
-    assertEquals(uid + ": SI prefix",
-        siPrefix,
-        unit.getSIPrefix(status));
-    status.errIfFailureAndReset("%s: SI prefix", identifier);
+    assertEquals(uid + ": SI or binary prefix",
+        unitPrefix,
+        unit.getPrefix(status));
+    status.errIfFailureAndReset("%s: SI or binary prefix", identifier);
     assertEquals(uid + ": Power",
         static_cast<int32_t>(power),
         static_cast<int32_t>(unit.getDimensionality(status)));
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index ccc3360..b3bfb87 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -1102,10 +1102,9 @@
          u"measure-unit/concentr-permille",                        //
          u"permille"},
 
-        // // TODO: binary prefixes not supported yet!
-        // {"Round-trip example from icu-units#35", //
-        //  u"unit/kibijoule-per-furlong",          //
-        //  u"unit/kibijoule-per-furlong"},
+        {"Round-trip example from icu-units#35", //
+         u"unit/kibijoule-per-furlong",          //
+         u"unit/kibijoule-per-furlong"},
     };
     for (auto &cas : cases) {
         IcuTestErrorCode status(*this, cas.msg);
diff --git a/icu4c/source/test/intltest/units_test.cpp b/icu4c/source/test/intltest/units_test.cpp
index a72fff3..8db46be 100644
--- a/icu4c/source/test/intltest/units_test.cpp
+++ b/icu4c/source/test/intltest/units_test.cpp
@@ -192,6 +192,14 @@
         {"gigabyte", "byte", 1.0, 1000000000},
         {"megawatt", "watt", 1.0, 1000000},
         {"megawatt", "kilowatt", 1.0, 1000},
+        // Binary Prefixes
+        {"kilobyte", "byte", 1, 1000},
+        {"kibibyte", "byte", 1, 1024},
+        {"mebibyte", "byte", 1, 1048576},
+        {"gibibyte", "kibibyte", 1, 1048576},
+        {"pebibyte", "tebibyte", 4, 4096},
+        {"zebibyte", "pebibyte", 1.0/16, 65536.0},
+        {"yobibyte", "exbibyte", 1, 1048576},
         // Mass
         {"gram", "kilogram", 1.0, 0.001},
         {"pound", "kilogram", 1.0, 0.453592},
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ConversionRates.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ConversionRates.java
index 0f69cdd..13fecf2 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ConversionRates.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ConversionRates.java
@@ -37,12 +37,16 @@
      * @param singleUnit
      * @return
      */
+    // In ICU4C, this is called loadCompoundFactor().
     private UnitConverter.Factor getFactorToBase(SingleUnitImpl singleUnit) {
         int power = singleUnit.getDimensionality();
-        MeasureUnit.SIPrefix siPrefix = singleUnit.getSiPrefix();
+        MeasureUnit.MeasurePrefix unitPrefix = singleUnit.getPrefix();
         UnitConverter.Factor result = UnitConverter.Factor.processFactor(mapToConversionRate.get(singleUnit.getSimpleUnitID()).getConversionRate());
 
-        return result.applySiPrefix(siPrefix).power(power); // NOTE: you must apply the SI prefixes before the power.
+        // Prefix before power, because:
+        // - square-kilometer to square-meter: (1000)^2
+        // - square-kilometer to square-foot (approximate): (3.28*1000)^2
+        return result.applyPrefix(unitPrefix).power(power);
     }
 
     public UnitConverter.Factor getFactorToBase(MeasureUnitImpl measureUnit) {
@@ -55,6 +59,7 @@
         return result;
     }
 
+    // In ICU4C, this functionality is found in loadConversionRate().
     protected BigDecimal getOffset(MeasureUnitImpl source, MeasureUnitImpl target, UnitConverter.Factor
             sourceToBase, UnitConverter.Factor targetToBase, UnitConverter.Convertibility convertibility) {
         if (convertibility != UnitConverter.Convertibility.CONVERTIBLE) return BigDecimal.valueOf(0);
@@ -124,7 +129,7 @@
         if (measureUnitImpl.getComplexity() != MeasureUnit.Complexity.SINGLE) return false;
         SingleUnitImpl singleUnit = measureUnitImpl.getSingleUnits().get(0);
 
-        if (singleUnit.getSiPrefix() != MeasureUnit.SIPrefix.ONE) return false;
+        if (singleUnit.getPrefix() != MeasureUnit.MeasurePrefix.ONE) return false;
         if (singleUnit.getDimensionality() != 1) return false;
 
         return true;
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
index 5658019..b5d1c00 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
@@ -388,6 +388,11 @@
         //     * unit", sawAnd is set to true. If not, it is left as is.
         private boolean fSawAnd = false;
 
+        // Cache the MeasurePrefix values array to make getPrefixFromTrieIndex()
+        // more efficient
+        private static MeasureUnit.MeasurePrefix[] measurePrefixValues =
+            MeasureUnit.MeasurePrefix.values();
+
         private UnitsParser(String identifier) {
             this.fSource = identifier;
 
@@ -403,6 +408,11 @@
             CharsTrieBuilder trieBuilder;
             trieBuilder = new CharsTrieBuilder();
 
+            // Add SI and binary prefixes
+            for (MeasureUnit.MeasurePrefix unitPrefix : measurePrefixValues) {
+                trieBuilder.add(unitPrefix.getIdentifier(), getTrieIndexForPrefix(unitPrefix));
+            }
+
             // Add syntax parts (compound, power prefixes)
             trieBuilder.add("-per-", CompoundPart.PER.getTrieIndex());
             trieBuilder.add("-", CompoundPart.TIMES.getTrieIndex());
@@ -425,12 +435,6 @@
             trieBuilder.add("pow14-", PowerPart.P14.getTrieIndex());
             trieBuilder.add("pow15-", PowerPart.P15.getTrieIndex());
 
-            // Add SI prefixes
-            for (MeasureUnit.SIPrefix siPrefix :
-                    MeasureUnit.SIPrefix.values()) {
-                trieBuilder.add(siPrefix.getIdentifier(), getTrieIndex(siPrefix));
-            }
-
             // Add simple units
             String[] simpleUnits = UnitsData.getSimpleUnits();
             for (int i = 0; i < simpleUnits.length; i++) {
@@ -459,18 +463,12 @@
 
         }
 
-        private static MeasureUnit.SIPrefix getSiPrefixFromTrieIndex(int trieIndex) {
-            for (MeasureUnit.SIPrefix element :
-                    MeasureUnit.SIPrefix.values()) {
-                if (getTrieIndex(element) == trieIndex)
-                    return element;
-            }
-
-            throw new IllegalArgumentException("Incorrect trieIndex");
+        private static MeasureUnit.MeasurePrefix getPrefixFromTrieIndex(int trieIndex) {
+            return measurePrefixValues[trieIndex - UnitsData.Constants.kPrefixOffset];
         }
 
-        private static int getTrieIndex(MeasureUnit.SIPrefix prefix) {
-            return prefix.getPower() + UnitsData.Constants.kSIPrefixOffset;
+        private static int getTrieIndexForPrefix(MeasureUnit.MeasurePrefix prefix) {
+            return prefix.ordinal() + UnitsData.Constants.kPrefixOffset;
         }
 
         private MeasureUnitImpl parse() {
@@ -524,9 +522,9 @@
             SingleUnitImpl result = new SingleUnitImpl();
 
             // state:
-            // 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
+            // 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit)
             // 1 = power token seen (will not accept another power token)
-            // 2 = SI prefix token seen (will not accept a power or SI prefix token)
+            // 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token)
             int state = 0;
 
             boolean atStart = fIndex == 0;
@@ -591,12 +589,12 @@
                         state = 1;
                         break;
 
-                    case TYPE_SI_PREFIX:
+                    case TYPE_PREFIX:
                         if (state > 1) {
                             throw new IllegalArgumentException();
                         }
 
-                        result.setSiPrefix(token.getSIPrefix());
+                        result.setPrefix(token.getPrefix());
                         state = 2;
                         break;
 
@@ -674,9 +672,9 @@
                 return this.type;
             }
 
-            public MeasureUnit.SIPrefix getSIPrefix() {
-                assert this.type == Type.TYPE_SI_PREFIX;
-                return getSiPrefixFromTrieIndex(this.fMatch);
+            public MeasureUnit.MeasurePrefix getPrefix() {
+                assert this.type == Type.TYPE_PREFIX;
+                return getPrefixFromTrieIndex(this.fMatch);
             }
 
             // Valid only for tokens with type TYPE_COMPOUND_PART.
@@ -700,6 +698,7 @@
             }
 
             public int getSimpleUnitIndex() {
+                assert this.type == Type.TYPE_SIMPLE_UNIT;
                 return this.fMatch - UnitsData.Constants.kSimpleUnitOffset;
             }
 
@@ -711,7 +710,7 @@
                 }
 
                 if (fMatch < UnitsData.Constants.kCompoundPartOffset) {
-                    return Type.TYPE_SI_PREFIX;
+                    return Type.TYPE_PREFIX;
                 }
                 if (fMatch < UnitsData.Constants.kInitialCompoundPartOffset) {
                     return Type.TYPE_COMPOUND_PART;
@@ -728,7 +727,7 @@
 
             enum Type {
                 TYPE_UNDEFINED,
-                TYPE_SI_PREFIX,
+                TYPE_PREFIX,
                 // Token type for "-per-", "-", and "-and-".
                 TYPE_COMPOUND_PART,
                 // Token type for "per-".
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/SingleUnitImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/SingleUnitImpl.java
index 6afb71b..fd35f44 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/SingleUnitImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/SingleUnitImpl.java
@@ -5,6 +5,9 @@
 
 import com.ibm.icu.util.MeasureUnit;
 
+/**
+ * A class representing a single unit (optional SI or binary prefix, and dimensionality).
+ */
 public class SingleUnitImpl {
     /**
      * Simple unit index, unique for every simple unit, -1 for the dimensionless
@@ -29,16 +32,16 @@
      */
     private int dimensionality = 1;
     /**
-     * SI Prefix
+     * SI or binary prefix.
      */
-    private MeasureUnit.SIPrefix siPrefix = MeasureUnit.SIPrefix.ONE;
+    private MeasureUnit.MeasurePrefix unitPrefix = MeasureUnit.MeasurePrefix.ONE;
 
     public SingleUnitImpl copy() {
         SingleUnitImpl result = new SingleUnitImpl();
         result.index = this.index;
         result.dimensionality = this.dimensionality;
         result.simpleUnitID = this.simpleUnitID;
-        result.siPrefix = this.siPrefix;
+        result.unitPrefix = this.unitPrefix;
 
         return result;
     }
@@ -71,7 +74,7 @@
             throw new IllegalArgumentException("Unit Identifier Syntax Error");
         }
 
-        result.append(this.getSiPrefix().getIdentifier());
+        result.append(this.getPrefix().getIdentifier());
         result.append(this.getSimpleUnitID());
 
         return result.toString();
@@ -103,10 +106,18 @@
         if (index > other.index) {
             return 1;
         }
-        if (this.getSiPrefix().getPower() < other.getSiPrefix().getPower()) {
+        // TODO(icu-units#70): revisit when fixing normalization. For now we're
+        // sorting binary prefixes before SI prefixes, for consistency with ICU4C.
+        if (this.getPrefix().getBase() < other.getPrefix().getBase()) {
+            return 1;
+        }
+        if (this.getPrefix().getBase() > other.getPrefix().getBase()) {
             return -1;
         }
-        if (this.getSiPrefix().getPower() > other.getSiPrefix().getPower()) {
+        if (this.getPrefix().getPower() < other.getPrefix().getPower()) {
+            return -1;
+        }
+        if (this.getPrefix().getPower() > other.getPrefix().getPower()) {
             return 1;
         }
         return 0;
@@ -115,8 +126,8 @@
     /**
      * Checks whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
      * <p>
-     * Units with the same base unit and SI prefix should match, except that they must also have
-     * the same dimensionality sign, such that we don't merge numerator and denominator.
+     * Units with the same base unit and SI or binary prefix should match, except that they must also
+     * have the same dimensionality sign, such that we don't merge numerator and denominator.
      */
     boolean isCompatibleWith(SingleUnitImpl other) {
         return (compareTo(other) == 0);
@@ -139,12 +150,12 @@
         this.dimensionality = dimensionality;
     }
 
-    public MeasureUnit.SIPrefix getSiPrefix() {
-        return siPrefix;
+    public MeasureUnit.MeasurePrefix getPrefix() {
+        return unitPrefix;
     }
 
-    public void setSiPrefix(MeasureUnit.SIPrefix siPrefix) {
-        this.siPrefix = siPrefix;
+    public void setPrefix(MeasureUnit.MeasurePrefix unitPrefix) {
+        this.unitPrefix = unitPrefix;
     }
 
     public int getIndex() {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitConverter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitConverter.java
index 4859bdb..e070982 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitConverter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitConverter.java
@@ -211,20 +211,24 @@
             }
         }
 
-        public Factor applySiPrefix(MeasureUnit.SIPrefix siPrefix) {
+        /** Apply SI or binary prefix to the Factor. */
+        public Factor applyPrefix(MeasureUnit.MeasurePrefix unitPrefix) {
             Factor result = this.copy();
-            if (siPrefix == MeasureUnit.SIPrefix.ONE) {
+            if (unitPrefix == MeasureUnit.MeasurePrefix.ONE) {
                 return result;
             }
 
-            BigDecimal siApplied = BigDecimal.valueOf(Math.pow(10.0, Math.abs(siPrefix.getPower())));
+            int base = unitPrefix.getBase();
+            int power = unitPrefix.getPower();
+            BigDecimal absFactor =
+                BigDecimal.valueOf(base).pow(Math.abs(power), DECIMAL128);
 
-            if (siPrefix.getPower() < 0) {
-                result.factorDen = this.factorDen.multiply(siApplied);
+            if (power < 0) {
+                result.factorDen = this.factorDen.multiply(absFactor);
                 return result;
             }
 
-            result.factorNum = this.factorNum.multiply(siApplied);
+            result.factorNum = this.factorNum.multiply(absFactor);
             return result;
         }
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsData.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsData.java
index 428fdaf..7391f56 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsData.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsData.java
@@ -109,6 +109,9 @@
      * Contains all the needed constants.
      */
     public static class Constants {
+        // TODO: consider moving the Trie-offset-related constants into
+        // MeasureUnitImpl.java, the only place they're being used?
+
         // Trie value offset for simple units, e.g. "gram", "nautical-mile",
         // "fluid-ounce-imperial".
         public static final int kSimpleUnitOffset = 512;
@@ -123,9 +126,9 @@
         // Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
         public final static int kCompoundPartOffset = 128;
 
-        // Trie value offset for SI Prefixes. This is big enough to ensure we only
-        // insert positive integers into the trie.
-        public static final int kSIPrefixOffset = 64;
+        // Trie value offset for SI or binary prefixes. This is big enough to
+        // ensure we only insert positive integers into the trie.
+        public static final int kPrefixOffset = 64;
 
 
         /* Tables Names*/
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
index 77adad4..26117eb 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
@@ -35,7 +35,6 @@
  * A unit such as length, mass, volume, currency, etc.  A unit is
  * coupled with a numeric amount to produce a Measure. MeasureUnit objects are immutable.
  * All subclasses must guarantee that. (However, subclassing is discouraged.)
-
  *
  * @see com.ibm.icu.util.Measure
  * @author Alan Liu
@@ -76,16 +75,16 @@
 
     /**
      * Enumeration for unit complexity. There are three levels:
-     * <p>
-     * - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
-     * square-kilometer, kilojoule, one-per-second.
-     * - COMPOUND: A unit composed of the product of multiple single units. Examples:
-     * meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
-     * - MIXED: A unit composed of the sum of multiple single units. Examples: foot-and-inch,
-     * hour-and-minute-and-second, degree-and-arcminute-and-arcsecond.
-     * <p>
+     * <ul>
+     * <li>SINGLE: A single unit, optionally with a power and/or SI or binary prefix.
+     * Examples: hectare, square-kilometer, kilojoule, per-second, mebibyte.</li>
+     * <li>COMPOUND: A unit composed of the product of multiple single units. Examples:
+     * meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.</li>
+     * <li>MIXED: A unit composed of the sum of multiple single units. Examples: foot-and-inch,
+     * hour-and-minute-and-second, degree-and-arcminute-and-arcsecond.</li>
+     * </ul>
      * The complexity determines which operations are available. For example, you cannot set the power
-     * or SI prefix of a compound unit.
+     * or prefix of a compound unit.
      *
      * @draft ICU 68
      * @provisional This API might change or be removed in a future release.
@@ -117,12 +116,12 @@
     }
 
     /**
-     * Enumeration for SI prefixes, such as "kilo".
+     * Enumeration for SI and binary prefixes, e.g. "kilo-", "nano-", "mebi-".
      *
-     * @draft ICU 68
+     * @draft ICU 69
      * @provisional This API might change or be removed in a future release.
      */
-    public enum SIPrefix {
+    public enum MeasurePrefix {
 
         /**
          * SI prefix: yotta, 10^24.
@@ -130,7 +129,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        YOTTA(24, "yotta"),
+        YOTTA(24, "yotta", 10),
 
         /**
          * SI prefix: zetta, 10^21.
@@ -138,7 +137,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        ZETTA(21, "zetta"),
+        ZETTA(21, "zetta", 10),
 
         /**
          * SI prefix: exa, 10^18.
@@ -146,7 +145,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        EXA(18, "exa"),
+        EXA(18, "exa", 10),
 
         /**
          * SI prefix: peta, 10^15.
@@ -154,7 +153,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        PETA(15, "peta"),
+        PETA(15, "peta", 10),
 
         /**
          * SI prefix: tera, 10^12.
@@ -162,7 +161,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        TERA(12, "tera"),
+        TERA(12, "tera", 10),
 
         /**
          * SI prefix: giga, 10^9.
@@ -170,7 +169,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        GIGA(9, "giga"),
+        GIGA(9, "giga", 10),
 
         /**
          * SI prefix: mega, 10^6.
@@ -178,7 +177,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        MEGA(6, "mega"),
+        MEGA(6, "mega", 10),
 
         /**
          * SI prefix: kilo, 10^3.
@@ -186,7 +185,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        KILO(3, "kilo"),
+        KILO(3, "kilo", 10),
 
         /**
          * SI prefix: hecto, 10^2.
@@ -194,7 +193,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        HECTO(2, "hecto"),
+        HECTO(2, "hecto", 10),
 
         /**
          * SI prefix: deka, 10^1.
@@ -202,7 +201,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        DEKA(1, "deka"),
+        DEKA(1, "deka", 10),
 
         /**
          * The absence of an SI prefix.
@@ -210,7 +209,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        ONE(0, ""),
+        ONE(0, "", 10),
 
         /**
          * SI prefix: deci, 10^-1.
@@ -218,7 +217,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        DECI(-1, "deci"),
+        DECI(-1, "deci", 10),
 
         /**
          * SI prefix: centi, 10^-2.
@@ -226,7 +225,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        CENTI(-2, "centi"),
+        CENTI(-2, "centi", 10),
 
         /**
          * SI prefix: milli, 10^-3.
@@ -234,7 +233,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        MILLI(-3, "milli"),
+        MILLI(-3, "milli", 10),
 
         /**
          * SI prefix: micro, 10^-6.
@@ -242,7 +241,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        MICRO(-6, "micro"),
+        MICRO(-6, "micro", 10),
 
         /**
          * SI prefix: nano, 10^-9.
@@ -250,7 +249,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        NANO(-9, "nano"),
+        NANO(-9, "nano", 10),
 
         /**
          * SI prefix: pico, 10^-12.
@@ -258,7 +257,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        PICO(-12, "pico"),
+        PICO(-12, "pico", 10),
 
         /**
          * SI prefix: femto, 10^-15.
@@ -266,7 +265,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        FEMTO(-15, "femto"),
+        FEMTO(-15, "femto", 10),
 
         /**
          * SI prefix: atto, 10^-18.
@@ -274,7 +273,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        ATTO(-18, "atto"),
+        ATTO(-18, "atto", 10),
 
         /**
          * SI prefix: zepto, 10^-21.
@@ -282,7 +281,7 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        ZEPTO(-21, "zepto"),
+        ZEPTO(-21, "zepto", 10),
 
         /**
          * SI prefix: yocto, 10^-24.
@@ -290,12 +289,78 @@
          * @draft ICU 68
          * @provisional This API might change or be removed in a future release.
          */
-        YOCTO(-24, "yocto");
+        YOCTO(-24, "yocto", 10),
 
+        /**
+         * IEC binary prefix: kibi, 1024^1.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        KIBI(1, "kibi", 1024),
+
+        /**
+         * IEC binary prefix: mebi, 1024^2.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        MEBI(2, "mebi", 1024),
+
+        /**
+         * IEC binary prefix: gibi, 1024^3.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        GIBI(3, "gibi", 1024),
+
+        /**
+         * IEC binary prefix: tebi, 1024^4.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        TEBI(4, "tebi", 1024),
+
+        /**
+         * IEC binary prefix: pebi, 1024^5.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        PEBI(5, "pebi", 1024),
+
+        /**
+         * IEC binary prefix: exbi, 1024^6.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        EXBI(6, "exbi", 1024),
+
+        /**
+         * IEC binary prefix: zebi, 1024^7.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        ZEBI(7, "zebi", 1024),
+
+        /**
+         * IEC binary prefix: yobi, 1024^8.
+         *
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        YOBI(8, "yobi", 1024);
+
+        private final int base;
         private final int power;
         private final String identifier;
 
-        SIPrefix(int power, String identifier) {
+        MeasurePrefix(int power, String identifier, int base) {
+            this.base = base;
             this.power = power;
             this.identifier = identifier;
         }
@@ -312,9 +377,23 @@
         }
 
         /**
-         * Returns the power of 10 of the prefix. For example, if the prefix is "centi", the power will be -2.
+         * Returns the base of the prefix. For example:
+         * - if the prefix is "centi", the base will be 10.
+         * - if the prefix is "gibi", the base will be 1024.
          *
-         * @draft ICU 68
+         * @draft ICU 69
+         * @provisional This API might change or be removed in a future release.
+         */
+        public int getBase() {
+            return base;
+        }
+
+        /**
+         * Returns the power of the prefix. For example:
+         * - if the prefix is "centi", the power will be -2.
+         * - if the prefix is "gibi", the power will be 3 (for base 1024).
+         *
+         * @draft ICU 69
          * @provisional This API might change or be removed in a future release.
          */
         public int getPower() {
@@ -426,41 +505,41 @@
     }
 
     /**
-     * Creates a MeasureUnit which is this SINGLE unit augmented with the specified SI prefix.
-     * For example, SI_PREFIX_KILO for "kilo".
-     * May return this if this unit already has that prefix.
+     * Creates a MeasureUnit which is this SINGLE unit augmented with the specified prefix.
+     * For example, MeasurePrefix.KILO for "kilo", or MeasurePrefix.KIBI for "kibi".
+     * May return `this` if this unit already has that prefix.
      * <p>
-     * There is sufficient locale data to format all standard SI prefixes.
+     * There is sufficient locale data to format all standard prefixes.
      * <p>
      * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
      * occur. For more information, see `Complexity`.
      *
-     * @param prefix The SI prefix, from SIPrefix.
+     * @param prefix The prefix, from MeasurePrefix.
      * @return A new SINGLE unit.
      * @throws UnsupportedOperationException if this unit is a COMPOUND or MIXED unit.
-     * @draft ICU 68
+     * @draft ICU 69
      * @provisional This API might change or be removed in a future release.
      */
-    public MeasureUnit withSIPrefix(SIPrefix prefix) {
+    public MeasureUnit withPrefix(MeasurePrefix prefix) {
         SingleUnitImpl singleUnit = getSingleUnitImpl();
-        singleUnit.setSiPrefix(prefix);
+        singleUnit.setPrefix(prefix);
         return singleUnit.build();
     }
 
     /**
-     * Returns the current SI prefix of this SINGLE unit. For example, if the unit has the SI prefix
-     * "kilo", then SI_PREFIX_KILO is returned.
+     * Returns the current SI or binary prefix of this SINGLE unit. For example,
+     * if the unit has the prefix "kilo", then MeasurePrefix.KILO is returned.
      * <p>
-     * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
-     * occur. For more information, see `Complexity`.
+     * NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an
+     * error will occur. For more information, see `Complexity`.
      *
-     * @return The SI prefix of this SINGLE unit, from SIPrefix.
+     * @return The prefix of this SINGLE unit, from MeasurePrefix.
      * @throws UnsupportedOperationException if the unit is COMPOUND or MIXED.
-     * @draft ICU 68
+     * @draft ICU 69
      * @provisional This API might change or be removed in a future release.
      */
-    public SIPrefix getSIPrefix() {
-        return getSingleUnitImpl().getSiPrefix();
+    public MeasurePrefix getPrefix() {
+        return getSingleUnitImpl().getPrefix();
     }
 
     /**
@@ -560,7 +639,7 @@
      * <p>
      * Examples:
      * - Given "meter-kilogram-per-second", three units will be returned: "meter",
-     * "kilogram", and "one-per-second".
+     * "kilogram", and "per-second".
      * - Given "hour+minute+second", three units will be returned: "hour", "minute",
      * and "second".
      * <p>
@@ -1982,6 +2061,7 @@
      * @return this object as a SingleUnitImpl.
      * @throws UnsupportedOperationException if this object could not be converted to a single unit.
      */
+    // In ICU4C, this is SingleUnitImpl::forMeasureUnit().
     private SingleUnitImpl getSingleUnitImpl() {
         if (measureUnitImpl == null) {
             return MeasureUnitImpl.forIdentifier(getIdentifier()).getSingleUnitImpl();
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MeasureUnitTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MeasureUnitTest.java
index 89c79a8..08a2885 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MeasureUnitTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/MeasureUnitTest.java
@@ -3481,27 +3481,61 @@
         }
 
         TestCase cases[] = {
-                // Correctly normalized identifiers should not change
-                new TestCase("square-meter-per-square-meter", "square-meter-per-square-meter"),
-                new TestCase("kilogram-meter-per-square-meter-square-second",
-                        "kilogram-meter-per-square-meter-square-second"),
-                new TestCase("square-mile-and-square-foot", "square-mile-and-square-foot"),
-                new TestCase("square-foot-and-square-mile", "square-foot-and-square-mile"),
-                new TestCase("per-cubic-centimeter", "per-cubic-centimeter"),
-                new TestCase("per-kilometer", "per-kilometer"),
+            // Correctly normalized identifiers should not change
+            new TestCase("square-meter-per-square-meter", "square-meter-per-square-meter"),
+            new TestCase("kilogram-meter-per-square-meter-square-second",
+                         "kilogram-meter-per-square-meter-square-second"),
+            new TestCase("square-mile-and-square-foot", "square-mile-and-square-foot"),
+            new TestCase("square-foot-and-square-mile", "square-foot-and-square-mile"),
+            new TestCase("per-cubic-centimeter", "per-cubic-centimeter"),
+            new TestCase("per-kilometer", "per-kilometer"),
 
-                // Normalization of power and per
-                new TestCase(
-                        "pow2-foot-and-pow2-mile", "square-foot-and-square-mile"),
-                new TestCase(
-                        "gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"),
-                new TestCase(
-                        "kilogram-per-meter-per-second", "kilogram-per-meter-second"),
+            // Normalization of power and per
+            new TestCase("pow2-foot-and-pow2-mile", "square-foot-and-square-mile"),
+            new TestCase("gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"),
+            new TestCase("kilogram-per-meter-per-second", "kilogram-per-meter-second"),
 
-                // TODO(ICU-21284): Add more test cases once the proper ranking is available.
+            // TODO(ICU-21284): Add more test cases once the proper ranking is available.
+
+            // Testing prefixes are parsed and produced correctly (ensures no
+            // collisions in the enum values)
+            new TestCase("yoctofoot", "yoctofoot"),
+            new TestCase("zeptofoot", "zeptofoot"),
+            new TestCase("attofoot", "attofoot"),
+            new TestCase("femtofoot", "femtofoot"),
+            new TestCase("picofoot", "picofoot"),
+            new TestCase("nanofoot", "nanofoot"),
+            new TestCase("microfoot", "microfoot"),
+            new TestCase("millifoot", "millifoot"),
+            new TestCase("centifoot", "centifoot"),
+            new TestCase("decifoot", "decifoot"),
+            new TestCase("foot", "foot"),
+            new TestCase("dekafoot", "dekafoot"),
+            new TestCase("hectofoot", "hectofoot"),
+            new TestCase("kilofoot", "kilofoot"),
+            new TestCase("megafoot", "megafoot"),
+            new TestCase("gigafoot", "gigafoot"),
+            new TestCase("terafoot", "terafoot"),
+            new TestCase("petafoot", "petafoot"),
+            new TestCase("exafoot", "exafoot"),
+            new TestCase("zettafoot", "zettafoot"),
+            new TestCase("yottafoot", "yottafoot"),
+            new TestCase("kibibyte", "kibibyte"),
+            new TestCase("mebibyte", "mebibyte"),
+            new TestCase("gibibyte", "gibibyte"),
+            new TestCase("tebibyte", "tebibyte"),
+            new TestCase("pebibyte", "pebibyte"),
+            new TestCase("exbibyte", "exbibyte"),
+            new TestCase("zebibyte", "zebibyte"),
+            new TestCase("yobibyte", "yobibyte"),
+
+            // Testing sort order of prefixes.
+            //
+            // TODO(icu-units#70): revisit when fixing normalization. For now we're
+            // just checking some consistency between C&J.
+            new TestCase("megafoot-mebifoot-kibifoot-kilofoot", "kibifoot-mebifoot-kilofoot-megafoot"),
         };
 
-
         for (TestCase testCase : cases) {
             MeasureUnit unit = MeasureUnit.forIdentifier(testCase.id);
 
@@ -3563,21 +3597,119 @@
     }
 
     @Test
+    public void TestIdentifierDetails() {
+        MeasureUnit joule = MeasureUnit.forIdentifier("joule");
+        assertEquals("Initial joule", "joule", joule.getIdentifier());
+    
+        // "Invalid prefix" test not needed: in Java we cannot pass a
+        // non-existant enum instance. (In C++ an int can be typecast.)
+
+        MeasureUnit unit = joule.withPrefix(MeasureUnit.MeasurePrefix.HECTO);
+        assertEquals("Joule with hecto prefix", "hectojoule", unit.getIdentifier());
+
+        unit = unit.withPrefix(MeasureUnit.MeasurePrefix.EXBI);
+        assertEquals("Joule with exbi prefix", "exbijoule", unit.getIdentifier());
+    }
+
+    @Test
+    public void TestPrefixes() {
+        class TestCase {
+            final MeasureUnit.MeasurePrefix prefix;
+            final int expectedBase;
+            final int expectedPower;
+
+            TestCase(MeasureUnit.MeasurePrefix prefix, int expectedBase, int expectedPower) {
+                this.prefix = prefix;
+                this.expectedBase = expectedBase;
+                this.expectedPower = expectedPower;
+            }
+        }
+
+        TestCase cases[] = {
+            new TestCase(MeasureUnit.MeasurePrefix.YOCTO, 10, -24),
+            new TestCase(MeasureUnit.MeasurePrefix.ZEPTO, 10, -21),
+            new TestCase(MeasureUnit.MeasurePrefix.ATTO, 10, -18),
+            new TestCase(MeasureUnit.MeasurePrefix.FEMTO, 10, -15),
+            new TestCase(MeasureUnit.MeasurePrefix.PICO, 10, -12),
+            new TestCase(MeasureUnit.MeasurePrefix.NANO, 10, -9),
+            new TestCase(MeasureUnit.MeasurePrefix.MICRO, 10, -6),
+            new TestCase(MeasureUnit.MeasurePrefix.MILLI, 10, -3),
+            new TestCase(MeasureUnit.MeasurePrefix.CENTI, 10, -2),
+            new TestCase(MeasureUnit.MeasurePrefix.DECI, 10, -1),
+            new TestCase(MeasureUnit.MeasurePrefix.ONE, 10, 0),
+            new TestCase(MeasureUnit.MeasurePrefix.DEKA, 10, 1),
+            new TestCase(MeasureUnit.MeasurePrefix.HECTO, 10, 2),
+            new TestCase(MeasureUnit.MeasurePrefix.KILO, 10, 3),
+            new TestCase(MeasureUnit.MeasurePrefix.MEGA, 10, 6),
+            new TestCase(MeasureUnit.MeasurePrefix.GIGA, 10, 9),
+            new TestCase(MeasureUnit.MeasurePrefix.TERA, 10, 12),
+            new TestCase(MeasureUnit.MeasurePrefix.PETA, 10, 15),
+            new TestCase(MeasureUnit.MeasurePrefix.EXA, 10, 18),
+            new TestCase(MeasureUnit.MeasurePrefix.ZETTA, 10, 21),
+            new TestCase(MeasureUnit.MeasurePrefix.YOTTA, 10, 24),
+            new TestCase(MeasureUnit.MeasurePrefix.KIBI, 1024, 1),
+            new TestCase(MeasureUnit.MeasurePrefix.MEBI, 1024, 2),
+            new TestCase(MeasureUnit.MeasurePrefix.GIBI, 1024, 3),
+            new TestCase(MeasureUnit.MeasurePrefix.TEBI, 1024, 4),
+            new TestCase(MeasureUnit.MeasurePrefix.PEBI, 1024, 5),
+            new TestCase(MeasureUnit.MeasurePrefix.EXBI, 1024, 6),
+            new TestCase(MeasureUnit.MeasurePrefix.ZEBI, 1024, 7),
+            new TestCase(MeasureUnit.MeasurePrefix.YOBI, 1024, 8),
+        };
+
+        for (TestCase testCase : cases) {
+            MeasureUnit m = MeasureUnit.AMPERE.withPrefix(testCase.prefix);
+            assertEquals("getPrefixPower()", testCase.expectedPower, m.getPrefix().getPower());
+            assertEquals("getPrefixBase()", testCase.expectedBase, m.getPrefix().getBase());
+        }
+    }
+
+    @Test
+    public void TestParseToBuiltIn() {
+        class TestCase {
+            final String identifier;
+            MeasureUnit expectedBuiltin;
+
+            TestCase(String identifier, MeasureUnit expectedBuiltin) {
+                this.identifier = identifier;
+                this.expectedBuiltin = expectedBuiltin;
+            }
+        }
+
+        TestCase cases[] = {
+            new TestCase("meter-per-second-per-second", MeasureUnit.METER_PER_SECOND_SQUARED),
+            new TestCase("meter-per-second-second", MeasureUnit.METER_PER_SECOND_SQUARED),
+            new TestCase("centimeter-centimeter", MeasureUnit.SQUARE_CENTIMETER),
+            new TestCase("square-foot", MeasureUnit.SQUARE_FOOT),
+            new TestCase("pow2-inch", MeasureUnit.SQUARE_INCH),
+            new TestCase("milligram-per-deciliter", MeasureUnit.MILLIGRAM_PER_DECILITER),
+            new TestCase("pound-force-per-pow2-inch", MeasureUnit.POUND_PER_SQUARE_INCH),
+            new TestCase("yard-pow2-yard", MeasureUnit.CUBIC_YARD),
+            new TestCase("square-yard-yard", MeasureUnit.CUBIC_YARD),
+        };
+
+        for (TestCase testCase : cases) {
+            MeasureUnit m = MeasureUnit.forIdentifier(testCase.identifier);
+            assertTrue(testCase.identifier + " parsed to builtin", m.equals(testCase.expectedBuiltin));
+        }
+    }
+
+    @Test
     public void TestCompoundUnitOperations() {
         MeasureUnit.forIdentifier("kilometer-per-second-joule");
 
         MeasureUnit kilometer = MeasureUnit.KILOMETER;
         MeasureUnit cubicMeter = MeasureUnit.CUBIC_METER;
-        MeasureUnit meter = kilometer.withSIPrefix(MeasureUnit.SIPrefix.ONE);
-        MeasureUnit centimeter1 = kilometer.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
-        MeasureUnit centimeter2 = meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
-        MeasureUnit cubicDecimeter = cubicMeter.withSIPrefix(MeasureUnit.SIPrefix.DECI);
+        MeasureUnit meter = kilometer.withPrefix(MeasureUnit.MeasurePrefix.ONE);
+        MeasureUnit centimeter1 = kilometer.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
+        MeasureUnit centimeter2 = meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
+        MeasureUnit cubicDecimeter = cubicMeter.withPrefix(MeasureUnit.MeasurePrefix.DECI);
 
-        verifySingleUnit(kilometer, MeasureUnit.SIPrefix.KILO, 1, "kilometer");
-        verifySingleUnit(meter, MeasureUnit.SIPrefix.ONE, 1, "meter");
-        verifySingleUnit(centimeter1, MeasureUnit.SIPrefix.CENTI, 1, "centimeter");
-        verifySingleUnit(centimeter2, MeasureUnit.SIPrefix.CENTI, 1, "centimeter");
-        verifySingleUnit(cubicDecimeter, MeasureUnit.SIPrefix.DECI, 3, "cubic-decimeter");
+        verifySingleUnit(kilometer, MeasureUnit.MeasurePrefix.KILO, 1, "kilometer");
+        verifySingleUnit(meter, MeasureUnit.MeasurePrefix.ONE, 1, "meter");
+        verifySingleUnit(centimeter1, MeasureUnit.MeasurePrefix.CENTI, 1, "centimeter");
+        verifySingleUnit(centimeter2, MeasureUnit.MeasurePrefix.CENTI, 1, "centimeter");
+        verifySingleUnit(cubicDecimeter, MeasureUnit.MeasurePrefix.DECI, 3, "cubic-decimeter");
 
         assertTrue("centimeter equality", centimeter1.equals( centimeter2));
         assertTrue("kilometer inequality", !centimeter1.equals( kilometer));
@@ -3587,10 +3719,10 @@
         MeasureUnit quarticKilometer = kilometer.withDimensionality(4);
         MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4);
 
-        verifySingleUnit(squareMeter, MeasureUnit.SIPrefix.ONE, 2, "square-meter");
-        verifySingleUnit(overCubicCentimeter, MeasureUnit.SIPrefix.CENTI, -3, "per-cubic-centimeter");
-        verifySingleUnit(quarticKilometer, MeasureUnit.SIPrefix.KILO, 4, "pow4-kilometer");
-        verifySingleUnit(overQuarticKilometer1, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
+        verifySingleUnit(squareMeter, MeasureUnit.MeasurePrefix.ONE, 2, "square-meter");
+        verifySingleUnit(overCubicCentimeter, MeasureUnit.MeasurePrefix.CENTI, -3, "per-cubic-centimeter");
+        verifySingleUnit(quarticKilometer, MeasureUnit.MeasurePrefix.KILO, 4, "pow4-kilometer");
+        verifySingleUnit(overQuarticKilometer1, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
 
         assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
 
@@ -3601,26 +3733,26 @@
                 .reciprocal();
         MeasureUnit overQuarticKilometer4 = meter.withDimensionality(4)
                 .reciprocal()
-                .withSIPrefix(MeasureUnit.SIPrefix.KILO);
+                .withPrefix(MeasureUnit.MeasurePrefix.KILO);
 
-        verifySingleUnit(overQuarticKilometer2, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
-        verifySingleUnit(overQuarticKilometer3, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
-        verifySingleUnit(overQuarticKilometer4, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
+        verifySingleUnit(overQuarticKilometer2, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
+        verifySingleUnit(overQuarticKilometer3, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
+        verifySingleUnit(overQuarticKilometer4, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
 
         assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer2));
         assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer3));
         assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer4));
 
         MeasureUnit kiloSquareSecond = MeasureUnit.SECOND
-                .withDimensionality(2).withSIPrefix(MeasureUnit.SIPrefix.KILO);
+                .withDimensionality(2).withPrefix(MeasureUnit.MeasurePrefix.KILO);
         MeasureUnit meterSecond = meter.product(kiloSquareSecond);
         MeasureUnit cubicMeterSecond1 = meter.withDimensionality(3).product(kiloSquareSecond);
-        MeasureUnit centimeterSecond1 = meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI).product(kiloSquareSecond);
+        MeasureUnit centimeterSecond1 = meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI).product(kiloSquareSecond);
         MeasureUnit secondCubicMeter = kiloSquareSecond.product(meter.withDimensionality(3));
-        MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI));
+        MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI));
         MeasureUnit secondCentimeterPerKilometer = secondCentimeter.product(kilometer.reciprocal());
 
-        verifySingleUnit(kiloSquareSecond, MeasureUnit.SIPrefix.KILO, 2, "square-kilosecond");
+        verifySingleUnit(kiloSquareSecond, MeasureUnit.MeasurePrefix.KILO, 2, "square-kilosecond");
         String meterSecondSub[] = {
                 "meter", "square-kilosecond"
         };
@@ -3655,7 +3787,7 @@
         assertTrue("reordering equality", cubicMeterSecond1.equals(secondCubicMeter));
         assertTrue("additional simple units inequality", !secondCubicMeter.equals(secondCentimeter));
 
-        // Don't allow get/set power or SI prefix on compound units
+        // Don't allow get/set power or SI or binary prefix on compound units
         try {
             meterSecond.getDimensionality();
             fail("UnsupportedOperationException must be thrown");
@@ -3671,14 +3803,14 @@
         }
 
         try {
-            meterSecond.getSIPrefix();
+            meterSecond.getPrefix();
             fail("UnsupportedOperationException must be thrown");
         } catch (UnsupportedOperationException e) {
             // Expecting an exception to be thrown
         }
 
         try {
-            meterSecond.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
+            meterSecond.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
             fail("UnsupportedOperationException must be thrown");
         } catch (UnsupportedOperationException e) {
             // Expecting an exception to be thrown
@@ -3709,12 +3841,12 @@
         // with others via product:
         MeasureUnit kilometer2 = kilometer.product(dimensionless);
 
-        verifySingleUnit(kilometer2, MeasureUnit.SIPrefix.KILO, 1, "kilometer");
+        verifySingleUnit(kilometer2, MeasureUnit.MeasurePrefix.KILO, 1, "kilometer");
         assertTrue("kilometer equality", kilometer.equals(kilometer2));
 
         // Test out-of-range powers
         MeasureUnit power15 = MeasureUnit.forIdentifier("pow15-kilometer");
-        verifySingleUnit(power15, MeasureUnit.SIPrefix.KILO, 15, "pow15-kilometer");
+        verifySingleUnit(power15, MeasureUnit.MeasurePrefix.KILO, 15, "pow15-kilometer");
 
         try {
             MeasureUnit.forIdentifier("pow16-kilometer");
@@ -3731,7 +3863,7 @@
         }
 
         MeasureUnit powerN15 = MeasureUnit.forIdentifier("per-pow15-kilometer");
-        verifySingleUnit(powerN15, MeasureUnit.SIPrefix.KILO, -15, "per-pow15-kilometer");
+        verifySingleUnit(powerN15, MeasureUnit.MeasurePrefix.KILO, -15, "per-pow15-kilometer");
 
         try {
             MeasureUnit.forIdentifier("per-pow16-kilometer");
@@ -3762,11 +3894,11 @@
         // product(dimensionless)
         MeasureUnit mile = MeasureUnit.MILE;
         mile = mile.product(dimensionless);
-        verifySingleUnit(mile, MeasureUnit.SIPrefix.ONE, 1, "mile");
+        verifySingleUnit(mile, MeasureUnit.MeasurePrefix.ONE, 1, "mile");
     }
 
-    private void verifySingleUnit(MeasureUnit singleMeasureUnit, MeasureUnit.SIPrefix prefix, int power, String identifier) {
-        assertEquals(identifier + ": SI prefix", prefix, singleMeasureUnit.getSIPrefix());
+    private void verifySingleUnit(MeasureUnit singleMeasureUnit, MeasureUnit.MeasurePrefix prefix, int power, String identifier) {
+        assertEquals(identifier + ": SI or binary prefix", prefix, singleMeasureUnit.getPrefix());
 
         assertEquals(identifier + ": Power", power, singleMeasureUnit.getDimensionality());
 
@@ -3805,12 +3937,12 @@
         assertEquals("nanogram", null, nanogram.getType());
         assertEquals("nanogram", "nanogram", nanogram.getIdentifier());
 
-        assertEquals("prefix of kilogram", MeasureUnit.SIPrefix.KILO, kilogram.getSIPrefix());
-        assertEquals("prefix of gram", MeasureUnit.SIPrefix.ONE, gram.getSIPrefix());
-        assertEquals("prefix of microgram", MeasureUnit.SIPrefix.MICRO, microgram.getSIPrefix());
-        assertEquals("prefix of nanogram", MeasureUnit.SIPrefix.NANO, nanogram.getSIPrefix());
+        assertEquals("prefix of kilogram", MeasureUnit.MeasurePrefix.KILO, kilogram.getPrefix());
+        assertEquals("prefix of gram", MeasureUnit.MeasurePrefix.ONE, gram.getPrefix());
+        assertEquals("prefix of microgram", MeasureUnit.MeasurePrefix.MICRO, microgram.getPrefix());
+        assertEquals("prefix of nanogram", MeasureUnit.MeasurePrefix.NANO, nanogram.getPrefix());
 
-        MeasureUnit tmp = kilogram.withSIPrefix(MeasureUnit.SIPrefix.MILLI);
+        MeasureUnit tmp = kilogram.withPrefix(MeasureUnit.MeasurePrefix.MILLI);
         assertEquals("Kilogram + milli should be milligram, got: " + tmp.getIdentifier(),
                 MeasureUnit.MILLIGRAM.getIdentifier(), tmp.getIdentifier());
     }
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
index 99e21cc..e9abbc2 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
@@ -296,6 +296,14 @@
                 new TestData("gigabyte", "byte", 1.0, 1000000000),
                 new TestData("megawatt", "watt", 1.0, 1000000),
                 new TestData("megawatt", "kilowatt", 1.0, 1000),
+                // Binary Prefixes
+                new TestData("kilobyte", "byte", 1, 1000),
+                new TestData("kibibyte", "byte", 1, 1024),
+                new TestData("mebibyte", "byte", 1, 1048576),
+                new TestData("gibibyte", "kibibyte", 1, 1048576),
+                new TestData("pebibyte", "tebibyte", 4, 4096),
+                new TestData("zebibyte", "pebibyte", 1.0/16, 65536.0),
+                new TestData("yobibyte", "exbibyte", 1, 1048576),
                 // Mass
                 new TestData("gram", "kilogram", 1.0, 0.001),
                 new TestData("pound", "kilogram", 1.0, 0.453592),
@@ -333,7 +341,13 @@
         ConversionRates conversionRates = new ConversionRates();
         for (TestData test : tests) {
             UnitConverter converter = new UnitConverter(test.source, test.target, conversionRates);
-            assertEquals(test.expected.doubleValue(), converter.convert(test.input).doubleValue(), (0.001));
+            double maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
+            if (test.expected.doubleValue() == 0) {
+                maxDelta = 1e-12;
+            }
+            assertEquals("testConverter: " + test.source + " to " + test.target,
+                         test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
+                         maxDelta);
         }
     }
 
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 dec74b9..0912f15 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
@@ -1067,10 +1067,9 @@
              "measure-unit/concentr-permille",                         //
              "permille"},
 
-            // // TODO: binary prefixes not supported yet!
-            // {"Round-trip example from icu-units#35", //
-            //  "unit/kibijoule-per-furlong",           //
-            //  "unit/kibijoule-per-furlong"},
+            {"Round-trip example from icu-units#35", //
+             "unit/kibijoule-per-furlong",           //
+             "unit/kibijoule-per-furlong"},
         };
         for (Object[] cas : cases) {
             String msg = (String)cas[0];