ICU-21182 Adding UNumberRangeFormatter (C API).

See #1193
diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj
index e009e21..88083d2 100644
--- a/icu4c/source/i18n/i18n.vcxproj
+++ b/icu4c/source/i18n/i18n.vcxproj
@@ -229,6 +229,7 @@
     <ClCompile Include="numparse_affixes.cpp" />
     <ClCompile Include="numparse_compositions.cpp" />
     <ClCompile Include="numparse_validators.cpp" />
+    <ClCompile Include="numrange_capi.cpp" />
     <ClCompile Include="numrange_fluent.cpp" />
     <ClCompile Include="numrange_impl.cpp" />
     <ClCompile Include="numfmt.cpp" />
diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters
index 50a06ee..9565f43 100644
--- a/icu4c/source/i18n/i18n.vcxproj.filters
+++ b/icu4c/source/i18n/i18n.vcxproj.filters
@@ -639,6 +639,9 @@
     <ClCompile Include="numparse_validators.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="numrange_capi.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="numrange_fluent.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj
index b4fd245..3c6734b 100644
--- a/icu4c/source/i18n/i18n_uwp.vcxproj
+++ b/icu4c/source/i18n/i18n_uwp.vcxproj
@@ -450,6 +450,7 @@
     <ClCompile Include="numparse_affixes.cpp" />
     <ClCompile Include="numparse_compositions.cpp" />
     <ClCompile Include="numparse_validators.cpp" />
+    <ClCompile Include="numrange_capi.cpp" />
     <ClCompile Include="numrange_fluent.cpp" />
     <ClCompile Include="numrange_impl.cpp" />
     <ClCompile Include="numfmt.cpp" />
diff --git a/icu4c/source/i18n/numrange_capi.cpp b/icu4c/source/i18n/numrange_capi.cpp
new file mode 100644
index 0000000..66ad14a
--- /dev/null
+++ b/icu4c/source/i18n/numrange_capi.cpp
@@ -0,0 +1,149 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include "fphdlimp.h"
+#include "number_utypes.h"
+#include "numparse_types.h"
+#include "formattedval_impl.h"
+#include "numrange_impl.h"
+#include "unicode/numberrangeformatter.h"
+#include "unicode/unumberrangeformatter.h"
+
+using namespace icu;
+using namespace icu::number;
+using namespace icu::number::impl;
+
+
+U_NAMESPACE_BEGIN
+namespace number {
+namespace impl {
+
+/**
+ * Implementation class for UNumberRangeFormatter. Wraps a LocalizedRangeNumberFormatter.
+ */
+struct UNumberRangeFormatterData : public UMemory,
+        // Magic number as ASCII == "NRF" (NumberRangeFormatter)
+        public IcuCApiHelper<UNumberRangeFormatter, UNumberRangeFormatterData, 0x4E524600> {
+    LocalizedNumberRangeFormatter fFormatter;
+};
+
+struct UFormattedNumberRangeImpl;
+
+// Magic number as ASCII == "FDN" (FormatteDNumber)
+typedef IcuCApiHelper<UFormattedNumberRange, UFormattedNumberRangeImpl, 0x46444E00> UFormattedNumberRangeApiHelper;
+
+struct UFormattedNumberRangeImpl : public UFormattedValueImpl, public UFormattedNumberRangeApiHelper {
+    UFormattedNumberRangeImpl();
+    ~UFormattedNumberRangeImpl();
+
+    FormattedNumberRange fImpl;
+    UFormattedNumberRangeData fData;
+};
+
+UFormattedNumberRangeImpl::UFormattedNumberRangeImpl()
+        : fImpl(&fData) {
+    fFormattedValue = &fImpl;
+}
+
+UFormattedNumberRangeImpl::~UFormattedNumberRangeImpl() {
+    // Disown the data from fImpl so it doesn't get deleted twice
+    fImpl.fData = nullptr;
+}
+
+}
+}
+U_NAMESPACE_END
+
+
+UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(
+    UFormattedNumberRange,
+    UFormattedNumberRangeImpl,
+    UFormattedNumberRangeApiHelper,
+    unumrf)
+
+
+U_CAPI UNumberRangeFormatter* U_EXPORT2
+unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+        const UChar* skeleton,
+        int32_t skeletonLen,
+        UNumberRangeCollapse collapse,
+        UNumberRangeIdentityFallback identityFallback,
+        const char* locale,
+        UParseError* perror,
+        UErrorCode* ec) {
+    auto* impl = new UNumberRangeFormatterData();
+    if (impl == nullptr) {
+        *ec = U_MEMORY_ALLOCATION_ERROR;
+        return nullptr;
+    }
+    // Readonly-alias constructor (first argument is whether we are NUL-terminated)
+    UnicodeString skeletonString(skeletonLen == -1, skeleton, skeletonLen);
+    impl->fFormatter = NumberRangeFormatter::withLocale(locale)
+        .numberFormatterBoth(NumberFormatter::forSkeleton(skeletonString, *perror, *ec))
+        .collapse(collapse)
+        .identityFallback(identityFallback);
+    return impl->exportForC();
+}
+
+U_CAPI void U_EXPORT2
+unumrf_formatDoubleRange(
+        const UNumberRangeFormatter* uformatter,
+        double first,
+        double second,
+        UFormattedNumberRange* uresult,
+        UErrorCode* ec) {
+    const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec);
+    auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+    if (U_FAILURE(*ec)) { return; }
+
+    result->fData.getStringRef().clear();
+    result->fData.quantity1.setToDouble(first);
+    result->fData.quantity2.setToDouble(second);
+    formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
+}
+
+U_CAPI void U_EXPORT2
+unumrf_formatDecimalRange(
+        const UNumberRangeFormatter* uformatter,
+        const char* first, int32_t firstLen,
+        const char* second, int32_t secondLen,
+        UFormattedNumberRange* uresult,
+        UErrorCode* ec) {
+    const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec);
+    auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+    if (U_FAILURE(*ec)) { return; }
+
+    result->fData.getStringRef().clear();
+    result->fData.quantity1.setToDecNumber({first, firstLen}, *ec);
+    result->fData.quantity2.setToDecNumber({second, secondLen}, *ec);
+    formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
+}
+
+U_CAPI UNumberRangeIdentityResult U_EXPORT2
+unumrf_resultGetIdentityResult(
+        const UFormattedNumberRange* uresult,
+        UErrorCode* ec) {
+    auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+    if (U_FAILURE(*ec)) {
+        return UNUM_IDENTITY_RESULT_COUNT;
+    }
+    return result->fData.identityResult;
+}
+
+U_CAPI void U_EXPORT2
+unumrf_close(UNumberRangeFormatter* f) {
+    UErrorCode localStatus = U_ZERO_ERROR;
+    const UNumberRangeFormatterData* impl = UNumberRangeFormatterData::validate(f, localStatus);
+    delete impl;
+}
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index c4b84de..335e5ee 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -133,6 +133,7 @@
 numparse_scientific.cpp
 numparse_symbols.cpp
 numparse_validators.cpp
+numrange_capi.cpp
 numrange_fluent.cpp
 numrange_impl.cpp
 numsys.cpp
diff --git a/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu4c/source/i18n/unicode/numberrangeformatter.h
index 4d436a7..bfbf274 100644
--- a/icu4c/source/i18n/unicode/numberrangeformatter.h
+++ b/icu4c/source/i18n/unicode/numberrangeformatter.h
@@ -16,6 +16,7 @@
 #include "unicode/formattedvalue.h"
 #include "unicode/fpositer.h"
 #include "unicode/numberformatter.h"
+#include "unicode/unumberrangeformatter.h"
 
 /**
  * \file
@@ -31,7 +32,7 @@
  *     .numberFormatterFirst(NumberFormatter::with().adoptUnit(MeasureUnit::createMeter()))
  *     .numberFormatterSecond(NumberFormatter::with().adoptUnit(MeasureUnit::createKilometer()))
  *     .locale("en-GB")
- *     .formatRange(750, 1.2, status)
+ *     .formatFormattableRange(750, 1.2, status)
  *     .toString(status);
  * // => "750 m - 1.2 km"
  * </pre>
@@ -44,128 +45,6 @@
  */
 
 
-/**
- * Defines how to merge fields that are identical across the range sign.
- *
- * @stable ICU 63
- */
-typedef enum UNumberRangeCollapse {
-    /**
-     * Use locale data and heuristics to determine how much of the string to collapse. Could end up collapsing none,
-     * some, or all repeated pieces in a locale-sensitive way.
-     *
-     * The heuristics used for this option are subject to change over time.
-     *
-     * @stable ICU 63
-     */
-    UNUM_RANGE_COLLAPSE_AUTO,
-
-    /**
-     * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
-     *
-     * @stable ICU 63
-     */
-    UNUM_RANGE_COLLAPSE_NONE,
-
-    /**
-     * Collapse the unit part of the number, but not the notation, if present. Example: "3.2 thousand – 5.3 thousand
-     * kilograms"
-     *
-     * @stable ICU 63
-     */
-    UNUM_RANGE_COLLAPSE_UNIT,
-
-    /**
-     * Collapse any field that is equal across the range sign. May introduce ambiguity on the magnitude of the
-     * number. Example: "3.2 – 5.3 thousand kilograms"
-     *
-     * @stable ICU 63
-     */
-    UNUM_RANGE_COLLAPSE_ALL
-} UNumberRangeCollapse;
-
-/**
- * Defines the behavior when the two numbers in the range are identical after rounding. To programmatically detect
- * when the identity fallback is used, compare the lower and upper BigDecimals via FormattedNumber.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
-typedef enum UNumberRangeIdentityFallback {
-    /**
-     * Show the number as a single value rather than a range. Example: "$5"
-     *
-     * @stable ICU 63
-     */
-    UNUM_IDENTITY_FALLBACK_SINGLE_VALUE,
-
-    /**
-     * Show the number using a locale-sensitive approximation pattern. If the numbers were the same before rounding,
-     * show the single value. Example: "~$5" or "$5"
-     *
-     * @stable ICU 63
-     */
-    UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE,
-
-    /**
-     * Show the number using a locale-sensitive approximation pattern. Use the range pattern always, even if the
-     * inputs are the same. Example: "~$5"
-     *
-     * @stable ICU 63
-     */
-    UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
-
-    /**
-     * Show the number as the range of two equal values. Use the range pattern always, even if the inputs are the
-     * same. Example (with RangeCollapse.NONE): "$5 – $5"
-     *
-     * @stable ICU 63
-     */
-    UNUM_IDENTITY_FALLBACK_RANGE
-} UNumberRangeIdentityFallback;
-
-/**
- * Used in the result class FormattedNumberRange to indicate to the user whether the numbers formatted in the range
- * were equal or not, and whether or not the identity fallback was applied.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
-typedef enum UNumberRangeIdentityResult {
-    /**
-     * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
-     *
-     * @stable ICU 63
-     * @see NumberRangeFormatter
-     */
-    UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING,
-
-    /**
-     * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
-     *
-     * @stable ICU 63
-     * @see NumberRangeFormatter
-     */
-    UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING,
-
-    /**
-     * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
-     *
-     * @stable ICU 63
-     * @see NumberRangeFormatter
-     */
-    UNUM_IDENTITY_RESULT_NOT_EQUAL,
-
-#ifndef U_HIDE_INTERNAL_API
-    /**
-     * The number of entries in this enum.
-     * @internal
-     */
-    UNUM_IDENTITY_RESULT_COUNT
-#endif
-
-} UNumberRangeIdentityResult;
-
 U_NAMESPACE_BEGIN
 
 namespace number {  // icu::number
@@ -182,6 +61,7 @@
 class DecimalQuantity;
 class UFormattedNumberRangeData;
 class NumberRangeFormatterImpl;
+struct UFormattedNumberRangeImpl;
 
 } // namespace impl
 
@@ -418,8 +298,8 @@
 
     /**
      * Sets the behavior when the two sides of the range are the same. This could happen if the same two numbers are
-     * passed to the formatRange function, or if different numbers are passed to the function but they become the same
-     * after rounding rules are applied. Possible values:
+     * passed to the formatFormattableRange function, or if different numbers are passed to the function but they
+     * become the same after rounding rules are applied. Possible values:
      * <p>
      * <ul>
      * <li>SINGLE_VALUE: "5 miles"</li>
@@ -820,6 +700,9 @@
 
     // To give LocalizedNumberRangeFormatter format methods access to this class's constructor:
     friend class LocalizedNumberRangeFormatter;
+
+    // To give C API access to internals
+    friend struct impl::UFormattedNumberRangeImpl;
 };
 
 /**
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index d91f148..83bb9b6 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -1,11 +1,12 @@
 // © 2018 and later: Unicode, Inc. and others.
 // License & terms of use: http://www.unicode.org/copyright.html
 
+#ifndef __UNUMBERFORMATTER_H__
+#define __UNUMBERFORMATTER_H__
+
 #include "unicode/utypes.h"
 
 #if !UCONFIG_NO_FORMATTING
-#ifndef __UNUMBERFORMATTER_H__
-#define __UNUMBERFORMATTER_H__
 
 #include "unicode/parseerr.h"
 #include "unicode/ufieldpositer.h"
@@ -725,5 +726,5 @@
 U_NAMESPACE_END
 #endif // U_SHOW_CPLUSPLUS_API
 
-#endif //__UNUMBERFORMATTER_H__
 #endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UNUMBERFORMATTER_H__
diff --git a/icu4c/source/i18n/unicode/unumberrangeformatter.h b/icu4c/source/i18n/unicode/unumberrangeformatter.h
new file mode 100644
index 0000000..6ed9cba
--- /dev/null
+++ b/icu4c/source/i18n/unicode/unumberrangeformatter.h
@@ -0,0 +1,432 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __UNUMBERRANGEFORMATTER_H__
+#define __UNUMBERRANGEFORMATTER_H__
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/parseerr.h"
+#include "unicode/ufieldpositer.h"
+#include "unicode/umisc.h"
+#include "unicode/uformattedvalue.h"
+#include "unicode/uformattable.h"
+
+
+/**
+ * \file
+ * \brief C-compatible API for localized number range formatting.
+ *
+ * This is the C-compatible version of the NumberRangeFormatter API. C++ users
+ * should include unicode/numberrangeformatter.h and use the proper C++ APIs.
+ *
+ * First create a UNumberRangeFormatter, which is immutable, and then format to
+ * a UFormattedNumberRange.
+ *
+ * Example code:
+ * <pre>
+ * // Setup:
+ * UErrorCode ec = U_ZERO_ERROR;
+ * UNumberRangeFormatter* uformatter = unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(
+ *     u"currency/USD precision-integer",
+ *     -1,
+ *     UNUM_RANGE_COLLAPSE_AUTO,
+ *     UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ *     "en-US",
+ *     NULL,
+ *     &ec);
+ * UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a double range:
+ * unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Get the result string:
+ * int32_t len;
+ * const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ * // str should equal "$3 – $5"
+ *
+ * // Cleanup:
+ * unumf_close(uformatter);
+ * unumf_closeResult(uresult);
+ * </pre>
+ *
+ * If you are a C++ user linking against the C libraries, you can use the LocalPointer versions of these
+ * APIs. The following example uses LocalPointer with the decimal number and field position APIs:
+ *
+ * <pre>
+ * // Setup:
+ * LocalUNumberRangeFormatterPointer uformatter(
+ *     unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(...));
+ * LocalUFormattedNumberRangePointer uresult(unumrf_openResult(&ec));
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a double number range:
+ * unumrf_formatDoubleRange(uformatter.getAlias(), 3.0, 5.0, uresult.getAlias(), &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // No need to do any cleanup since we are using LocalPointer.
+ * </pre>
+ *
+ * You can also get field positions. For more information, see uformattedvalue.h.
+ */
+
+/**
+ * Defines how to merge fields that are identical across the range sign.
+ *
+ * @stable ICU 63
+ */
+typedef enum UNumberRangeCollapse {
+    /**
+     * Use locale data and heuristics to determine how much of the string to collapse. Could end up collapsing none,
+     * some, or all repeated pieces in a locale-sensitive way.
+     *
+     * The heuristics used for this option are subject to change over time.
+     *
+     * @stable ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_AUTO,
+
+    /**
+     * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
+     *
+     * @stable ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_NONE,
+
+    /**
+     * Collapse the unit part of the number, but not the notation, if present. Example: "3.2 thousand – 5.3 thousand
+     * kilograms"
+     *
+     * @stable ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_UNIT,
+
+    /**
+     * Collapse any field that is equal across the range sign. May introduce ambiguity on the magnitude of the
+     * number. Example: "3.2 – 5.3 thousand kilograms"
+     *
+     * @stable ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_ALL
+} UNumberRangeCollapse;
+
+/**
+ * Defines the behavior when the two numbers in the range are identical after rounding. To programmatically detect
+ * when the identity fallback is used, compare the lower and upper BigDecimals via FormattedNumber.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityFallback {
+    /**
+     * Show the number as a single value rather than a range. Example: "$5"
+     *
+     * @stable ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_SINGLE_VALUE,
+
+    /**
+     * Show the number using a locale-sensitive approximation pattern. If the numbers were the same before rounding,
+     * show the single value. Example: "~$5" or "$5"
+     *
+     * @stable ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE,
+
+    /**
+     * Show the number using a locale-sensitive approximation pattern. Use the range pattern always, even if the
+     * inputs are the same. Example: "~$5"
+     *
+     * @stable ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+
+    /**
+     * Show the number as the range of two equal values. Use the range pattern always, even if the inputs are the
+     * same. Example (with RangeCollapse.NONE): "$5 – $5"
+     *
+     * @stable ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_RANGE
+} UNumberRangeIdentityFallback;
+
+/**
+ * Used in the result class FormattedNumberRange to indicate to the user whether the numbers formatted in the range
+ * were equal or not, and whether or not the identity fallback was applied.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityResult {
+    /**
+     * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
+     *
+     * @stable ICU 63
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING,
+
+    /**
+     * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
+     *
+     * @stable ICU 63
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING,
+
+    /**
+     * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
+     *
+     * @stable ICU 63
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_RESULT_NOT_EQUAL,
+
+#ifndef U_HIDE_INTERNAL_API
+    /**
+     * The number of entries in this enum.
+     * @internal
+     */
+    UNUM_IDENTITY_RESULT_COUNT
+#endif
+
+} UNumberRangeIdentityResult;
+
+
+#ifndef U_HIDE_DRAFT_API
+
+struct UNumberRangeFormatter;
+/**
+ * C-compatible version of icu::number::LocalizedNumberRangeFormatter.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @draft ICU 68
+ */
+typedef struct UNumberRangeFormatter UNumberRangeFormatter;
+
+
+struct UFormattedNumberRange;
+/**
+ * C-compatible version of icu::number::FormattedNumberRange.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @draft ICU 68
+ */
+typedef struct UFormattedNumberRange UFormattedNumberRange;
+
+
+/**
+ * Creates a new UNumberFormatter for the given skeleton string, collapse option, identity fallback
+ * option, and locale. This is currently the only method for creating a new UNumberRangeFormatter.
+ *
+ * Objects of type UNumberRangeFormatter returned by this method are threadsafe.
+ *
+ * For more details on skeleton strings, see the documentation in numberrangeformatter.h. For more
+ * details on the usage of this API, see the documentation at the top of unumberrangeformatter.h.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param skeleton The skeleton string, like u"percent precision-integer"
+ * @param skeletonLen The number of UChars in the skeleton string, or -1 if it is NUL-terminated.
+ * @param collapse Option for how to merge affixes (if unsure, use UNUM_RANGE_COLLAPSE_AUTO)
+ * @param identityFallback Option for resolving when both sides of the range are equal.
+ * @param locale The NUL-terminated locale ID.
+ * @param perror A parse error struct populated if an error occurs when parsing. Can be NULL.
+ *               If no error occurs, perror->offset will be set to -1.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE UNumberRangeFormatter* U_EXPORT2
+unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+    const UChar* skeleton,
+    int32_t skeletonLen,
+    UNumberRangeCollapse collapse,
+    UNumberRangeIdentityFallback identityFallback,
+    const char* locale,
+    UParseError* perror,
+    UErrorCode* ec);
+
+
+/**
+ * Creates an object to hold the result of a UNumberRangeFormatter
+ * operation. The object can be used repeatedly; it is cleared whenever
+ * passed to a format function.
+ *
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE UFormattedNumberRange* U_EXPORT2
+unumrf_openResult(UErrorCode* ec);
+
+
+/**
+ * Uses a UNumberRangeFormatter to format a range of doubles.
+ *
+ * The UNumberRangeFormatter can be shared between threads. Each thread should have its own local
+ * UFormattedNumberRange, however, for storing the result of the formatting operation.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param uformatter A formatter object; see unumberrangeformatter.h.
+ * @param first The first (usually smaller) number in the range.
+ * @param second The second (usually larger) number in the range.
+ * @param uresult The object that will be mutated to store the result; see unumrf_openResult.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_formatDoubleRange(
+    const UNumberRangeFormatter* uformatter,
+    double first,
+    double second,
+    UFormattedNumberRange* uresult,
+    UErrorCode* ec);
+
+
+/**
+ * Uses a UNumberRangeFormatter to format a range of decimal numbers.
+ *
+ * With a decimal number string, you can specify an input with arbitrary precision.
+ *
+ * The UNumberRangeFormatter can be shared between threads. Each thread should have its own local
+ * UFormattedNumberRange, however, for storing the result of the formatting operation.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param uformatter A formatter object; see unumberrangeformatter.h.
+ * @param first The first (usually smaller) number in the range.
+ * @param firstLen The length of the first decimal number string.
+ * @param second The second (usually larger) number in the range.
+ * @param secondLen The length of the second decimal number string.
+ * @param uresult The object that will be mutated to store the result; see unumrf_openResult.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_formatDecimalRange(
+    const UNumberRangeFormatter* uformatter,
+    const char* first,
+    int32_t firstLen,
+    const char* second,
+    int32_t secondLen,
+    UFormattedNumberRange* uresult,
+    UErrorCode* ec);
+
+
+/**
+ * Returns a representation of a UFormattedNumberRange as a UFormattedValue,
+ * which can be subsequently passed to any API requiring that type.
+ *
+ * The returned object is owned by the UFormattedNumberRange and is valid
+ * only as long as the UFormattedNumber is present and unchanged in memory.
+ *
+ * You can think of this method as a cast between types.
+ *
+ * @param uresult The object containing the formatted number range.
+ * @param ec Set if an error occurs.
+ * @return A UFormattedValue owned by the input object.
+ * @draft ICU 68
+ */
+U_STABLE const UFormattedValue* U_EXPORT2
+unumrf_resultAsValue(const UFormattedNumberRange* uresult, UErrorCode* ec);
+
+
+/**
+ * Extracts the identity result from a UFormattedNumberRange.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
+ *
+ * @param uresult The object containing the formatted number range.
+ * @param ec Set if an error occurs.
+ * @return The identity result; see UNumberRangeIdentityResult.
+ * @draft ICU 68
+ */
+U_STABLE UNumberRangeIdentityResult U_EXPORT2
+unumrf_resultGetIdentityResult(
+    const UFormattedNumberRange* uresult,
+    UErrorCode* ec);
+
+
+
+// TODO(ICU-20775): Propose these as API.
+// NOTE: This is not currently implemented.
+// U_DRAFT int32_t U_EXPORT2
+// unumf_resultGetFirstDecimal(const UFormattedNumberRange* uresult, char* buffer, int32_t bufferCapacity,
+//                             UErrorCode* ec);
+// U_DRAFT int32_t U_EXPORT2
+// unumf_resultGetSecondDecimal(const UFormattedNumberRange* uresult, char* buffer, int32_t bufferCapacity,
+//                              UErrorCode* ec);
+
+
+/**
+ * Releases the UNumberFormatter created by unumf_openForSkeletonAndLocale().
+ *
+ * @param uformatter An object created by unumf_openForSkeletonAndLocale().
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_close(UNumberRangeFormatter* uformatter);
+
+
+/**
+ * Releases the UFormattedNumber created by unumf_openResult().
+ *
+ * @param uresult An object created by unumf_openResult().
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_closeResult(UFormattedNumberRange* uresult);
+
+
+#if U_SHOW_CPLUSPLUS_API
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUNumberRangeFormatterPointer
+ * "Smart pointer" class; closes a UNumberFormatter via unumf_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUNumberRangeFormatterPointer uformatter(
+ *     unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(...));
+ * // no need to explicitly call unumrf_close()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 68
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberRangeFormatterPointer, UNumberRangeFormatter, unumrf_close);
+
+/**
+ * \class LocalUFormattedNumberPointer
+ * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUFormattedNumberRangePointer uresult(unumrf_openResult(...));
+ * // no need to explicitly call unumrf_closeResult()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 68
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberRangePointer, UFormattedNumberRange, unumrf_closeResult);
+
+U_NAMESPACE_END
+#endif // U_SHOW_CPLUSPLUS_API
+
+#endif // U_HIDE_DRAFT_API
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UNUMBERRANGEFORMATTER_H__
diff --git a/icu4c/source/test/cintltst/Makefile.in b/icu4c/source/test/cintltst/Makefile.in
index a803837..34f8ea2 100644
--- a/icu4c/source/test/cintltst/Makefile.in
+++ b/icu4c/source/test/cintltst/Makefile.in
@@ -55,7 +55,7 @@
 utexttst.o ucsdetst.o spooftest.o \
 cbiditransformtst.o \
 cgendtst.o \
-unumberformattertst.o uformattedvaluetst.o
+unumberformattertst.o uformattedvaluetst.o unumberrangeformattertst.o
 
 DEPS = $(OBJECTS:.o=.d)
 
diff --git a/icu4c/source/test/cintltst/cformtst.c b/icu4c/source/test/cintltst/cformtst.c
index f4e62b7..f9c3ac3 100644
--- a/icu4c/source/test/cintltst/cformtst.c
+++ b/icu4c/source/test/cintltst/cformtst.c
@@ -41,6 +41,7 @@
 void addUListFmtTest(TestNode** root);
 void addUNumberFormatterTest(TestNode** root);
 void addUFormattedValueTest(TestNode** root);
+void addUNumberRangeFormatterTest(TestNode** root);
 
 void addFormatTest(TestNode** root);
 
@@ -65,6 +66,7 @@
     addUListFmtTest(root);
     addUNumberFormatterTest(root);
     addUFormattedValueTest(root);
+    addUNumberRangeFormatterTest(root);
 }
 /*Internal functions used*/
 
diff --git a/icu4c/source/test/cintltst/cintltst.vcxproj b/icu4c/source/test/cintltst/cintltst.vcxproj
index 9444d3a..2f54863 100644
--- a/icu4c/source/test/cintltst/cintltst.vcxproj
+++ b/icu4c/source/test/cintltst/cintltst.vcxproj
@@ -175,6 +175,7 @@
     <ClCompile Include="ulistfmttest.c" />
     <ClCompile Include="unumberformattertst.c" />
     <ClCompile Include="uformattedvaluetst.c" />
+    <ClCompile Include="unumberrangeformattertst.c" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="cbiditst.h" />
diff --git a/icu4c/source/test/cintltst/cintltst.vcxproj.filters b/icu4c/source/test/cintltst/cintltst.vcxproj.filters
index b541163..d39436d 100644
--- a/icu4c/source/test/cintltst/cintltst.vcxproj.filters
+++ b/icu4c/source/test/cintltst/cintltst.vcxproj.filters
@@ -225,6 +225,9 @@
     <ClInclude Include="uformattedvaluetst.c">
       <Filter>formatting</Filter>
     </ClInclude>
+    <ClInclude Include="unumberrangeformattertst.c">
+      <Filter>formatting</Filter>
+    </ClInclude>
     <ClCompile Include="cldrtest.c">
       <Filter>locales &amp; resources</Filter>
     </ClCompile>
diff --git a/icu4c/source/test/cintltst/unumberrangeformattertst.c b/icu4c/source/test/cintltst/unumberrangeformattertst.c
new file mode 100644
index 0000000..8dcc5c1
--- /dev/null
+++ b/icu4c/source/test/cintltst/unumberrangeformattertst.c
@@ -0,0 +1,152 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include <stdio.h>
+#include "unicode/unumberformatter.h"
+#include "unicode/unumberrangeformatter.h"
+#include "unicode/umisc.h"
+#include "unicode/unum.h"
+#include "unicode/ustring.h"
+#include "cformtst.h"
+#include "cintltst.h"
+#include "cmemory.h"
+
+static void TestExampleCode(void);
+
+static void TestFormattedValue(void);
+
+static void TestSkeletonParseError(void);
+
+void addUNumberRangeFormatterTest(TestNode** root);
+
+#define TESTCASE(x) addTest(root, &x, "tsformat/unumberrangeformatter/" #x)
+
+void addUNumberRangeFormatterTest(TestNode** root) {
+    TESTCASE(TestExampleCode);
+    TESTCASE(TestFormattedValue);
+    TESTCASE(TestSkeletonParseError);
+}
+
+
+static void TestExampleCode() {
+    // This is the example code given in unumberrangeformatter.h.
+
+    // Setup:
+    UErrorCode ec = U_ZERO_ERROR;
+    UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+        u"currency/USD precision-integer",
+        -1,
+        UNUM_RANGE_COLLAPSE_AUTO,
+        UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+        "en-US",
+        NULL,
+        &ec);
+    UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+    assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+
+    // Format a double range:
+    unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
+    assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+
+    // Get the result string:
+    int32_t len;
+    const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec);
+    assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+    assertUEquals("Should produce expected string result", u"$3 – $5", str);
+
+    // Cleanup:
+    unumrf_close(uformatter);
+    unumrf_closeResult(uresult);
+}
+
+
+static void TestFormattedValue() {
+    UErrorCode ec = U_ZERO_ERROR;
+    UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+        u"K",
+        -1,
+        UNUM_RANGE_COLLAPSE_AUTO,
+        UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+        "en-US",
+        NULL,
+        &ec);
+    assertSuccessCheck("Should create without error", &ec, TRUE);
+    UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+    assertSuccess("Should create result without error", &ec);
+
+    // Test the decimal number code path, too
+    unumrf_formatDecimalRange(uformatter, "5.5e4", -1, "1.5e5", -1, uresult, &ec);
+
+    if (assertSuccessCheck("Should format without error", &ec, TRUE)) {
+        const UFormattedValue* fv = unumrf_resultAsValue(uresult, &ec);
+        assertSuccess("Should convert without error", &ec);
+        static const UFieldPosition expectedFieldPositions[] = {
+            // field, begin index, end index
+            {UNUM_INTEGER_FIELD, 0, 2},
+            {UNUM_COMPACT_FIELD, 2, 3},
+            {UNUM_INTEGER_FIELD, 6, 9},
+            {UNUM_COMPACT_FIELD, 9, 10}};
+        checkFormattedValue(
+            "FormattedNumber as FormattedValue",
+            fv,
+            u"55K – 150K",
+            UFIELD_CATEGORY_NUMBER,
+            expectedFieldPositions,
+            UPRV_LENGTHOF(expectedFieldPositions));
+    }
+
+    assertIntEquals("Identity result should match",
+        UNUM_IDENTITY_RESULT_NOT_EQUAL,
+        unumrf_resultGetIdentityResult(uresult, &ec));
+
+    // cleanup:
+    unumrf_closeResult(uresult);
+    unumrf_close(uformatter);
+}
+
+
+static void TestSkeletonParseError() {
+    UErrorCode ec = U_ZERO_ERROR;
+    UNumberRangeFormatter* uformatter;
+    UParseError perror;
+
+    // The UParseError can be null. The following should not segfault.
+    uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+        u".00 measure-unit/typo",
+        -1, 
+        UNUM_RANGE_COLLAPSE_AUTO,
+        UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+        "en",
+        NULL,
+        &ec);
+    unumrf_close(uformatter);
+
+    // Now test the behavior.
+    ec = U_ZERO_ERROR;
+    uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+        u".00 measure-unit/typo",
+        -1, 
+        UNUM_RANGE_COLLAPSE_AUTO,
+        UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+        "en",
+        &perror,
+        &ec);
+
+    assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec);
+    assertIntEquals("Should have correct skeleton error offset", 17, perror.offset);
+    assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext);
+    assertUEquals("Should have correct post context", u"typo", perror.postContext);
+
+    // cleanup:
+    unumrf_close(uformatter);
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt
index abaa74b..9dfad52 100644
--- a/icu4c/source/test/depstest/dependencies.txt
+++ b/icu4c/source/test/depstest/dependencies.txt
@@ -1000,7 +1000,7 @@
 
 group: number_skeletons
     # Number skeleton support; separated from numberformatter
-    number_skeletons.o number_capi.o number_asformat.o
+    number_skeletons.o number_capi.o number_asformat.o numrange_capi.o
   deps
     numberformatter
     units_extra