]> granicus.if.org Git - icu/commitdiff
ICU-13678 Cleaning up multiplier implementation and adding public API.
authorShane Carr <shane@unicode.org>
Tue, 3 Apr 2018 04:38:16 +0000 (04:38 +0000)
committerShane Carr <shane@unicode.org>
Tue, 3 Apr 2018 04:38:16 +0000 (04:38 +0000)
X-SVN-Rev: 41188

28 files changed:
icu4c/source/i18n/Makefile.in
icu4c/source/i18n/number_decimalquantity.cpp
icu4c/source/i18n/number_decimalquantity.h
icu4c/source/i18n/number_fluent.cpp
icu4c/source/i18n/number_mapper.cpp
icu4c/source/i18n/number_multiplier.cpp
icu4c/source/i18n/number_multiplier.h
icu4c/source/i18n/number_utils.h
icu4c/source/i18n/numparse_impl.cpp
icu4c/source/i18n/numparse_impl.h
icu4c/source/i18n/numparse_validators.h
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/number/MacroProps.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierFormatHandler.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierImpl.java [deleted file]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierHandler.java [deleted file]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierParseHandler.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
icu4j/main/classes/core/src/com/ibm/icu/number/Multiplier.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterSettings.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
icu4j/main/classes/core/src/com/ibm/icu/number/Rounder.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java

index eaa5d0683ee09afc86cb249cff44f427984d38e5..44236b50474d0c5421784928a48ca283f97e0c96 100644 (file)
@@ -103,13 +103,13 @@ number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \
 number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \
 number_padding.o number_patternmodifier.o number_patternstring.o \
 number_rounding.o number_scientific.o number_stringbuilder.o \
+number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \
 double-conversion.o double-conversion-bignum-dtoa.o double-conversion-bignum.o \
 double-conversion-cached-powers.o double-conversion-diy-fp.o \
 double-conversion-fast-dtoa.o double-conversion-strtod.o \
-numparse_stringsegment.o numparse_unisets.o numparse_parsednumber.o \
-numparse_impl.o numparse_symbols.o numparse_decimal.o numparse_scientific.o \
-numparse_currency.o numparse_affixes.o numparse_compositions.o numparse_validators.o \
-number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \
+numparse_stringsegment.o numparse_unisets.o numparse_parsednumber.o numparse_impl.o \
+numparse_symbols.o numparse_decimal.o numparse_scientific.o numparse_currency.o \
+numparse_affixes.o numparse_compositions.o numparse_validators.o \
 
 
 ## Header files to install
index 40ad848ff4e4045857df77e2b6aecdc437950776..ca91c839a69d53a2cbf5aa67444993472b87146c 100644 (file)
@@ -198,10 +198,18 @@ void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode ro
     roundToMagnitude(-maxFrac, roundingMode, status);
 }
 
-void DecimalQuantity::multiplyBy(int32_t multiplicand) {
+void DecimalQuantity::multiplyBy(double multiplicand) {
     if (isInfinite() || isZero() || isNaN()) {
         return;
     }
+    // Cover a few simple cases...
+    if (multiplicand == 1) {
+        return;
+    } else if (multiplicand == -1) {
+        negate();
+        return;
+    }
+    // Do math for all other cases...
     // TODO: Should we convert to decNumber instead?
     double temp = toDouble();
     temp *= multiplicand;
index b205778e19a251ee26754a0bcd96c326479f121f..4d8bb270d7a27ef3216f1606a2dd2a9c18930a09 100644 (file)
@@ -93,7 +93,7 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
      *
      * @param multiplicand The value by which to multiply.
      */
-    void multiplyBy(int32_t multiplicand);
+    void multiplyBy(double multiplicand);
 
     /** Flips the sign from positive to negative and back. */
     void negate();
index c5feb5e4380b4f963de568379898df593a217cc5..81cf90a84718634e8aa52a4d7e7acf1f6e937757 100644 (file)
@@ -233,6 +233,20 @@ Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorD
     return move;
 }
 
+template<typename Derived>
+Derived NumberFormatterSettings<Derived>::multiplier(const Multiplier& multiplier) const& {
+    Derived copy(*this);
+    copy.fMacros.multiplier = multiplier;
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberFormatterSettings<Derived>::multiplier(const Multiplier& multiplier)&& {
+    Derived move(std::move(*this));
+    move.fMacros.multiplier = multiplier;
+    return move;
+}
+
 template<typename Derived>
 Derived NumberFormatterSettings<Derived>::padding(const Padder& padder) const& {
     Derived copy(*this);
index 84774011011f402e6f3580f612c458701397d558..9acf60382b2527b18ef24077ab688af601326abe 100644 (file)
@@ -257,11 +257,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
     // MULTIPLIERS //
     /////////////////
 
-    if (properties.magnitudeMultiplier != 0) {
-        macros.multiplier = Multiplier::magnitude(properties.magnitudeMultiplier);
-    } else if (properties.multiplier != 1) {
-        macros.multiplier = Multiplier::integer(properties.multiplier);
-    }
+    macros.multiplier = multiplierFromProperties(properties);
 
     //////////////////////
     // PROPERTY EXPORTS //
index ca445ba4dda44bedd0394888911a90d3bab5afe5..85fbd9e6ac0eddae64db2450843363fe3b744d4f 100644 (file)
 
 #include "number_types.h"
 #include "number_multiplier.h"
+#include "numparse_validators.h"
 
 using namespace icu;
 using namespace icu::number;
 using namespace icu::number::impl;
+using namespace icu::numparse::impl;
 
 
-Multiplier::Multiplier(int32_t magnitudeMultiplier, int32_t multiplier)
-        : magnitudeMultiplier(magnitudeMultiplier), multiplier(multiplier) {}
+Multiplier::Multiplier(int32_t magnitude, double arbitrary)
+        : fMagnitude(magnitude), fArbitrary(arbitrary) {}
 
-Multiplier Multiplier::magnitude(int32_t magnitudeMultiplier) {
-    return {magnitudeMultiplier, 1};
+Multiplier Multiplier::none() {
+    return {0, 1};
 }
 
-Multiplier Multiplier::integer(int32_t multiplier) {
-    return {0, multiplier};
+Multiplier Multiplier::powerOfTen(int32_t power) {
+    return {power, 1};
+}
+
+Multiplier Multiplier::arbitraryDecimal(StringPiece multiplicand) {
+    // TODO: Fix this hack
+    UErrorCode localError = U_ZERO_ERROR;
+    DecimalQuantity dq;
+    dq.setToDecNumber(multiplicand, localError);
+    return {0, dq.toDouble()};
+}
+
+Multiplier Multiplier::arbitraryDouble(double multiplicand) {
+    return {0, multiplicand};
+}
+
+void Multiplier::applyTo(impl::DecimalQuantity& quantity) const {
+    quantity.adjustMagnitude(fMagnitude);
+    quantity.multiplyBy(fArbitrary);
+}
+
+void Multiplier::applyReciprocalTo(impl::DecimalQuantity& quantity) const {
+    quantity.adjustMagnitude(-fMagnitude);
+    if (fArbitrary != 0) {
+        quantity.multiplyBy(1 / fArbitrary);
+    }
 }
 
 
-void MultiplierChain::setAndChain(const Multiplier& multiplier, const MicroPropsGenerator* parent) {
+void
+MultiplierFormatHandler::setAndChain(const Multiplier& multiplier, const MicroPropsGenerator* parent) {
     this->multiplier = multiplier;
     this->parent = parent;
 }
 
-void
-MultiplierChain::processQuantity(DecimalQuantity& quantity, MicroProps& micros, UErrorCode& status) const {
+void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
+                                              UErrorCode& status) const {
     parent->processQuantity(quantity, micros, status);
-    quantity.adjustMagnitude(multiplier.magnitudeMultiplier);
-    if (multiplier.multiplier != 1) {
-        quantity.multiplyBy(multiplier.multiplier);
+    multiplier.applyTo(quantity);
+}
+
+
+// NOTE: MultiplierParseHandler is declared in the header numparse_validators.h
+MultiplierParseHandler::MultiplierParseHandler(::icu::number::Multiplier multiplier)
+        : fMultiplier(multiplier) {}
+
+void MultiplierParseHandler::postProcess(ParsedNumber& result) const {
+    if (!result.quantity.bogus) {
+        fMultiplier.applyReciprocalTo(result.quantity);
+        // NOTE: It is okay if the multiplier was negative.
     }
 }
 
+UnicodeString MultiplierParseHandler::toString() const {
+    return u"<Multiplier>";
+}
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index 93d103dd84a5aca923ca26ffebd8696dbcda9bca..6baa68659167446357285b6c487c6db3ba79a8f3 100644 (file)
@@ -8,14 +8,18 @@
 #define __SOURCE_NUMBER_MULTIPLIER_H__
 
 #include "numparse_types.h"
+#include "number_decimfmtprops.h"
 
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
 
 
-class MultiplierChain : public MicroPropsGenerator, public UMemory {
+/**
+ * Wraps a {@link Multiplier} for use in the number formatting pipeline.
+ */
+class MultiplierFormatHandler : public MicroPropsGenerator, public UMemory {
   public:
-    void setAndChain(const Multiplier& other, const MicroPropsGenerator* parent);
+    void setAndChain(const Multiplier& multiplier, const MicroPropsGenerator* parent);
 
     void processQuantity(DecimalQuantity& quantity, MicroProps& micros,
                          UErrorCode& status) const U_OVERRIDE;
@@ -26,6 +30,18 @@ class MultiplierChain : public MicroPropsGenerator, public UMemory {
 };
 
 
+/** Gets a Multiplier from a DecimalFormatProperties. In Java, defined in RoundingUtils.java */
+static inline Multiplier multiplierFromProperties(const DecimalFormatProperties& properties) {
+    if (properties.magnitudeMultiplier != 0) {
+        return Multiplier::powerOfTen(properties.magnitudeMultiplier);
+    } else if (properties.multiplier != 1) {
+        return Multiplier::arbitraryDouble(properties.multiplier);
+    } else {
+        return Multiplier::none();
+    }
+}
+
+
 } // namespace impl
 } // namespace number
 U_NAMESPACE_END
index 8f74692cc6129837dca3608170691f6bebfaedf1..23c6fcf7ec87b9609f425bd8e29d418d85567274 100644 (file)
@@ -75,7 +75,7 @@ struct MicroProps : public MicroPropsGenerator {
         ScientificModifier scientificModifier;
         EmptyModifier emptyWeakModifier{false};
         EmptyModifier emptyStrongModifier{true};
-        MultiplierChain multiplier;
+        MultiplierFormatHandler multiplier;
     } helpers;
 
 
index 89db7001a34eaa921d1231340f1b30ef59555818..c917aad1764b383652865bf5a20ac9ac1b9090fc 100644 (file)
@@ -180,14 +180,10 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
                 properties.decimalSeparatorAlwaysShown || properties.maximumFractionDigits != 0;
         parser->addMatcher(parser->fLocalValidators.decimalSeparator = {patternHasDecimalSeparator});
     }
-
-    // TODO: MULTIPLIER
-//    if (properties.getMultiplier() != null) {
-//        // We need to use a math context in order to prevent non-terminating decimal expansions.
-//        // This is only used when dividing by the multiplier.
-//        parser.addMatcher(new MultiplierHandler(properties.getMultiplier(),
-//                RoundingUtils.getMathContextOr34Digits(properties)));
-//    }
+    // NOTE: Don't look at magnitude multiplier here. That is performed when percent sign is seen.
+    if (properties.multiplier != 1) {
+        parser->addMatcher(parser->fLocalValidators.multiplier = {multiplierFromProperties(properties)});
+    }
 
     parser->freeze();
     return parser.orphan();
index 308a2ffcf81b34ba7bbdfe4ed4a0701a595e6afb..748b9415ecb80398146fd03484c4ede3c64f7472 100644 (file)
@@ -17,6 +17,7 @@
 #include "number_decimfmtprops.h"
 #include "unicode/localpointer.h"
 #include "numparse_validators.h"
+#include "number_multiplier.h"
 
 U_NAMESPACE_BEGIN namespace numparse {
 namespace impl {
@@ -78,6 +79,7 @@ class NumberParserImpl : public MutableMatcherCollection {
         RequireDecimalSeparatorValidator decimalSeparator;
         RequireExponentValidator exponent;
         RequireNumberValidator number;
+        MultiplierParseHandler multiplier;
     } fLocalValidators;
 
     explicit NumberParserImpl(parse_flags_t parseFlags);
index 817ec9cb8d678bb2d0012076a968dd54b651d00f..d3bc63aceb308be00d0ae531d605cd6c30ac8ef4 100644 (file)
@@ -77,6 +77,26 @@ class RequireNumberValidator : public ValidationMatcher, public UMemory {
 };
 
 
+/**
+ * Wraps a {@link Multiplier} for use in the number parsing pipeline.
+ *
+ * NOTE: Implemented in number_multiplier.cpp
+ */
+class MultiplierParseHandler : public ValidationMatcher, public UMemory {
+  public:
+    MultiplierParseHandler() = default;  // leaves instance in valid but undefined state
+
+    MultiplierParseHandler(::icu::number::Multiplier multiplier);
+
+    void postProcess(ParsedNumber& result) const U_OVERRIDE;
+
+    UnicodeString toString() const U_OVERRIDE;
+
+  private:
+    ::icu::number::Multiplier fMultiplier;
+};
+
+
 } // namespace impl
 } // namespace numparse
 U_NAMESPACE_END
index 51ead000cf4c1d8f2d71bf71c1f8d9f73718764e..54698d382eacd2411dbb35dcb5fd91d1cf38bf80 100644 (file)
@@ -86,6 +86,7 @@ namespace impl {
 
 // Forward declarations:
 class NumberParserImpl;
+class MultiplierParseHandler;
 
 }
 }
@@ -142,7 +143,7 @@ class NumberStringBuilder;
 class AffixPatternProvider;
 class NumberPropertyMapper;
 struct DecimalFormatProperties;
-class MultiplierChain;
+class MultiplierFormatHandler;
 class CurrencySymbols;
 class GeneratorHelpers;
 
@@ -895,7 +896,6 @@ class U_I18N_API IntegerWidth : public UMemory {
      *            The minimum number of places before the decimal separator.
      * @return An IntegerWidth for chaining or passing to the NumberFormatter integerWidth() setter.
      * @draft ICU 60
-     * @see NumberFormatter
      */
     static IntegerWidth zeroFillTo(int32_t minInt);
 
@@ -909,7 +909,6 @@ class U_I18N_API IntegerWidth : public UMemory {
      *            truncation.
      * @return An IntegerWidth for passing to the NumberFormatter integerWidth() setter.
      * @draft ICU 60
-     * @see NumberFormatter
      */
     IntegerWidth truncateAt(int32_t maxInt);
 
@@ -966,6 +965,94 @@ class U_I18N_API IntegerWidth : public UMemory {
     friend class impl::GeneratorHelpers;
 };
 
+/**
+ * A class that defines a quantity by which a number should be multiplied when formatting.
+ *
+ * <p>
+ * To create a Multiplier, use one of the factory methods.
+ *
+ * @draft ICU 62
+ */
+class U_I18N_API Multiplier : public UMemory {
+  public:
+    /**
+     * Do not change the value of numbers when formatting or parsing.
+     *
+     * @return A Multiplier to prevent any multiplication.
+     * @draft ICU 62
+     */
+    static Multiplier none();
+
+    /**
+     * Multiply numbers by 100 before formatting. Useful for combining with a percent unit:
+     *
+     * <pre>
+     * NumberFormatter::with().unit(NoUnit::percent()).multiplier(Multiplier::powerOfTen(2))
+     * </pre>
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     */
+    static Multiplier powerOfTen(int32_t power);
+
+    /**
+     * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions.
+     *
+     * This method takes a string in a decimal number format with syntax
+     * as defined in the Decimal Arithmetic Specification, available at
+     * http://speleotrove.com/decimal
+     *
+     * Also see the version of this method that takes a double.
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     */
+    static Multiplier arbitraryDecimal(StringPiece multiplicand);
+
+    /**
+     * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions.
+     *
+     * This method takes a double; also see the version of this method that takes an exact decimal.
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     */
+    static Multiplier arbitraryDouble(double multiplicand);
+
+  private:
+    int32_t fMagnitude;
+    double fArbitrary;
+
+    Multiplier(int32_t magnitude, double arbitrary);
+
+    Multiplier() : fMagnitude(0), fArbitrary(1) {}
+
+    bool isValid() const {
+        return fMagnitude != 0 || fArbitrary != 1;
+    }
+
+    void applyTo(impl::DecimalQuantity& quantity) const;
+
+    void applyReciprocalTo(impl::DecimalQuantity& quantity) const;
+
+    // To allow MacroProps/MicroProps to initialize empty instances:
+    friend struct impl::MacroProps;
+    friend struct impl::MicroProps;
+
+    // To allow NumberFormatterImpl to access isBogus() and perform other operations:
+    friend class impl::NumberFormatterImpl;
+
+    // To allow the helper class MultiplierFormatHandler access to private fields:
+    friend class impl::MultiplierFormatHandler;
+
+    // To allow access to the skeleton generation code:
+    friend class impl::GeneratorHelpers;
+
+    // To allow access to parsing code:
+    friend class ::icu::numparse::impl::NumberParserImpl;
+    friend class ::icu::numparse::impl::MultiplierParseHandler;
+};
+
 namespace impl {
 
 // Do not enclose entire SymbolsWrapper with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
@@ -1208,41 +1295,6 @@ class U_I18N_API Padder : public UMemory {
 };
 
 // Do not enclose entire MacroProps with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
-/** @internal */
-class U_I18N_API Multiplier : public UMemory {
-  public:
-    /** @internal */
-    static Multiplier magnitude(int32_t magnitudeMultiplier);
-
-    /** @internal */
-    static Multiplier integer(int32_t multiplier);
-
-  private:
-    int32_t magnitudeMultiplier;
-    int32_t multiplier;
-
-    Multiplier(int32_t magnitudeMultiplier, int32_t multiplier);
-
-    Multiplier() : magnitudeMultiplier(0), multiplier(1) {}
-
-    bool isValid() const {
-        return magnitudeMultiplier != 0 || multiplier != 1;
-    }
-
-    // To allow MacroProps/MicroProps to initialize empty instances:
-    friend struct MacroProps;
-    friend struct MicroProps;
-
-    // To allow NumberFormatterImpl to access isBogus() and perform other operations:
-    friend class impl::NumberFormatterImpl;
-
-    // To allow the helper class MultiplierChain access to private fields:
-    friend class impl::MultiplierChain;
-
-    // To allow access to the skeleton generation code:
-    friend class impl::GeneratorHelpers;
-};
-
 /** @internal */
 struct U_I18N_API MacroProps : public UMemory {
     /** @internal */
@@ -1280,8 +1332,10 @@ struct U_I18N_API MacroProps : public UMemory {
     /** @internal */
     UNumberDecimalSeparatorDisplay decimal = UNUM_DECIMAL_SEPARATOR_COUNT;
 
-    Multiplier multiplier;  // = Multiplier();  (bogus)
+    /** @internal */
+    Multiplier multiplier;  // = Multiplier();  (benign value)
 
+    /** @internal */
     AffixPatternProvider* affixProvider = nullptr;  // no ownership
 
     /** @internal */
@@ -1835,11 +1889,48 @@ class U_I18N_API NumberFormatterSettings {
      * @param style
      *            The decimal separator display strategy to use when rendering numbers.
      * @return The fluent chain.
-     * @see #sign
+     * @see #decimal
      * @draft ICU 62
      */
     Derived decimal(const UNumberDecimalSeparatorDisplay &style) &&;
 
+    /**
+     * Sets a multiplier to be used to scale the number by an arbitrary amount before formatting. Most
+     * common values:
+     *
+     * <ul>
+     * <li>Multiply by 100: useful for percentages.
+     * <li>Multiply by an arbitrary value: useful for unit conversions.
+     * </ul>
+     *
+     * <p>
+     * Pass an element from a {@link Multiplier} factory method to this setter. For example:
+     *
+     * <pre>
+     * NumberFormatter::with().multiplier(Multiplier::powerOfTen(2))
+     * </pre>
+     *
+     * <p>
+     * The default is to not apply any multiplier.
+     *
+     * @param style
+     *            The decimal separator display strategy to use when rendering numbers.
+     * @return The fluent chain
+     * @draft ICU 60
+     */
+    Derived multiplier(const Multiplier &style) const &;
+
+    /**
+     * Overload of multiplier() for use on an rvalue reference.
+     *
+     * @param style
+     *            The multiplier separator display strategy to use when rendering numbers.
+     * @return The fluent chain.
+     * @see #multiplier
+     * @draft ICU 62
+     */
+    Derived multiplier(const Multiplier &style) &&;
+
 #ifndef U_HIDE_INTERNAL_API
 
     /**
index e1a84aab1e3331a546a7cfa6e9bcf2948f3e2685..2da93bcbb819ef552e4f1603249585a2a706f6d3 100644 (file)
@@ -64,6 +64,7 @@ class NumberFormatterApiTest : public IntlTest {
     //void symbolsOverride();
     void sign();
     void decimal();
+    void multiplier();
     void locale();
     void formatTypes();
     void errors();
index a6d1a3553c6b40a28d257c96e901ccac460f8132..bf650087886a3f50ddcf116389bb6644b0ace50b 100644 (file)
@@ -77,6 +77,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
         //TESTCASE_AUTO(symbolsOverride);
         TESTCASE_AUTO(sign);
         TESTCASE_AUTO(decimal);
+        TESTCASE_AUTO(multiplier);
         TESTCASE_AUTO(locale);
         TESTCASE_AUTO(formatTypes);
         TESTCASE_AUTO(errors);
@@ -1909,6 +1910,83 @@ void NumberFormatterApiTest::decimal() {
             u"0.");
 }
 
+void NumberFormatterApiTest::multiplier() {
+    assertFormatDescending(
+            u"Multiplier None",
+            u"",
+            NumberFormatter::with().multiplier(Multiplier::none()),
+            Locale::getEnglish(),
+            u"87,650",
+            u"8,765",
+            u"876.5",
+            u"87.65",
+            u"8.765",
+            u"0.8765",
+            u"0.08765",
+            u"0.008765",
+            u"0");
+
+    assertFormatDescending(
+            u"Multiplier Power of Ten",
+            nullptr,
+            NumberFormatter::with().multiplier(Multiplier::powerOfTen(6)),
+            Locale::getEnglish(),
+            u"87,650,000,000",
+            u"8,765,000,000",
+            u"876,500,000",
+            u"87,650,000",
+            u"8,765,000",
+            u"876,500",
+            u"87,650",
+            u"8,765",
+            u"0");
+
+    assertFormatDescending(
+            u"Multiplier Arbitrary Double",
+            nullptr,
+            NumberFormatter::with().multiplier(Multiplier::arbitraryDouble(5.2)),
+            Locale::getEnglish(),
+            u"455,780",
+            u"45,578",
+            u"4,557.8",
+            u"455.78",
+            u"45.578",
+            u"4.5578",
+            u"0.45578",
+            u"0.045578",
+            u"0");
+
+    assertFormatDescending(
+            u"Multiplier Arbitrary BigDecimal",
+            nullptr,
+            NumberFormatter::with().multiplier(Multiplier::arbitraryDecimal({"5.2", -1})),
+            Locale::getEnglish(),
+            u"455,780",
+            u"45,578",
+            u"4,557.8",
+            u"455.78",
+            u"45.578",
+            u"4.5578",
+            u"0.45578",
+            u"0.045578",
+            u"0");
+
+    assertFormatDescending(
+            u"Multiplier Zero",
+            nullptr,
+            NumberFormatter::with().multiplier(Multiplier::arbitraryDouble(0)),
+            Locale::getEnglish(),
+            u"0",
+            u"0",
+            u"0",
+            u"0",
+            u"0",
+            u"0",
+            u"0",
+            u"0",
+            u"0");
+}
+
 void NumberFormatterApiTest::locale() {
     // Coverage for the locale setters.
     UErrorCode status = U_ZERO_ERROR;
index fa0f7648ca7a9a897018a5119045f7b057617d2c..47448c2bbdeda3c9e8da20da61e0bd45c20ab108 100644 (file)
@@ -4,6 +4,7 @@ package com.ibm.icu.impl.number;
 
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.number.IntegerWidth;
+import com.ibm.icu.number.Multiplier;
 import com.ibm.icu.number.Notation;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
@@ -25,8 +26,8 @@ public class MacroProps implements Cloneable {
     public UnitWidth unitWidth;
     public SignDisplay sign;
     public DecimalSeparatorDisplay decimal;
+    public Multiplier multiplier;
     public AffixPatternProvider affixProvider; // not in API; for JDK compatibility mode only
-    public MultiplierImpl multiplier; // not in API; for JDK compatibility mode only
     public PluralRules rules; // not in API; could be made public in the future
     public Long threshold; // not in API; controls internal self-regulation threshold
     public ULocale loc;
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierFormatHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierFormatHandler.java
new file mode 100644 (file)
index 0000000..fb7389a
--- /dev/null
@@ -0,0 +1,25 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number;
+
+import com.ibm.icu.number.Multiplier;
+
+/**
+ * Wraps a {@link Multiplier} for use in the number formatting pipeline.
+ */
+public class MultiplierFormatHandler implements MicroPropsGenerator {
+    final Multiplier multiplier;
+    final MicroPropsGenerator parent;
+
+    public MultiplierFormatHandler(Multiplier multiplier, MicroPropsGenerator parent) {
+        this.multiplier = multiplier;
+        this.parent = parent;
+    }
+
+    @Override
+    public MicroProps processQuantity(DecimalQuantity quantity) {
+        MicroProps micros = parent.processQuantity(quantity);
+        multiplier.applyTo(quantity);
+        return micros;
+    }
+}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MultiplierImpl.java
deleted file mode 100644 (file)
index dd495d1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number;
-
-import java.math.BigDecimal;
-
-public class MultiplierImpl implements MicroPropsGenerator {
-    final int magnitudeMultiplier;
-    final BigDecimal bigDecimalMultiplier;
-    final MicroPropsGenerator parent;
-
-    public MultiplierImpl(int magnitudeMultiplier) {
-        this.magnitudeMultiplier = magnitudeMultiplier;
-        this.bigDecimalMultiplier = null;
-        parent = null;
-    }
-
-    public MultiplierImpl(BigDecimal bigDecimalMultiplier) {
-        this.magnitudeMultiplier = 0;
-        this.bigDecimalMultiplier = bigDecimalMultiplier;
-        parent = null;
-    }
-
-    private MultiplierImpl(MultiplierImpl base, MicroPropsGenerator parent) {
-        this.magnitudeMultiplier = base.magnitudeMultiplier;
-        this.bigDecimalMultiplier = base.bigDecimalMultiplier;
-        this.parent = parent;
-    }
-
-    public MicroPropsGenerator copyAndChain(MicroPropsGenerator parent) {
-        return new MultiplierImpl(this, parent);
-    }
-
-    @Override
-    public MicroProps processQuantity(DecimalQuantity quantity) {
-        MicroProps micros = parent.processQuantity(quantity);
-        quantity.adjustMagnitude(magnitudeMultiplier);
-        if (bigDecimalMultiplier != null) {
-            quantity.multiplyBy(bigDecimalMultiplier);
-        }
-        return micros;
-    }
-}
index b9a3cdb6da1b53b53cc346c48ef1e47a66c7512d..7d9ca686c31029a2cd3c91296812bedbef08d36f 100644 (file)
@@ -6,6 +6,8 @@ import java.math.BigDecimal;
 import java.math.MathContext;
 import java.math.RoundingMode;
 
+import com.ibm.icu.number.Multiplier;
+
 /** @author sffc */
 public class RoundingUtils {
 
@@ -147,6 +149,14 @@ public class RoundingUtils {
         }
     }
 
+    /** The default MathContext, unlimited-precision version. */
+    public static final MathContext DEFAULT_MATH_CONTEXT_UNLIMITED
+            = MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[DEFAULT_ROUNDING_MODE.ordinal()];
+
+    /** The default MathContext, 34-digit version. */
+    public static final MathContext DEFAULT_MATH_CONTEXT_34_DIGITS
+            = MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[DEFAULT_ROUNDING_MODE.ordinal()];
+
     /**
      * Gets the user-specified math context out of the property bag. If there is none, falls back to a
      * math context with unlimited precision and the user-specified rounding mode, which defaults to
@@ -198,4 +208,15 @@ public class RoundingUtils {
     public static MathContext mathContextUnlimited(RoundingMode roundingMode) {
         return MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[roundingMode.ordinal()];
     }
+
+    public static Multiplier multiplierFromProperties(DecimalFormatProperties properties) {
+        MathContext mc = getMathContextOr34Digits(properties);
+        if (properties.getMagnitudeMultiplier() != 0) {
+            return Multiplier.powerOfTen(properties.getMagnitudeMultiplier()).withMathContext(mc);
+        } else if (properties.getMultiplier() != null) {
+            return Multiplier.arbitrary(properties.getMultiplier()).withMathContext(mc);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierHandler.java
deleted file mode 100644 (file)
index e2c2250..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number.parse;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-
-/**
- * @author sffc
- *
- */
-public class MultiplierHandler extends ValidationMatcher {
-
-    private final BigDecimal multiplier;
-    private final MathContext mc;
-    private final boolean isNegative;
-
-    public MultiplierHandler(BigDecimal multiplier, MathContext mc) {
-        this.multiplier = BigDecimal.ONE.divide(multiplier, mc).abs();
-        this.mc = mc;
-        isNegative = multiplier.signum() < 0;
-    }
-
-    @Override
-    public void postProcess(ParsedNumber result) {
-        if (result.quantity != null) {
-            result.quantity.multiplyBy(multiplier);
-            result.quantity.roundToMagnitude(result.quantity.getMagnitude() - mc.getPrecision(), mc);
-            if (isNegative) {
-                result.flags ^= ParsedNumber.FLAG_NEGATIVE;
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "<MultiplierHandler " + multiplier + ">";
-    }
-}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierParseHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MultiplierParseHandler.java
new file mode 100644 (file)
index 0000000..054cdf4
--- /dev/null
@@ -0,0 +1,30 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+import com.ibm.icu.number.Multiplier;
+
+/**
+ * Wraps a {@link Multiplier} for use in the number parsing pipeline.
+ */
+public class MultiplierParseHandler extends ValidationMatcher {
+
+    private final Multiplier multiplier;
+
+    public MultiplierParseHandler(Multiplier multiplier) {
+        this.multiplier = multiplier;
+    }
+
+    @Override
+    public void postProcess(ParsedNumber result) {
+        if (result.quantity != null) {
+            multiplier.applyReciprocalTo(result.quantity);
+            // NOTE: It is okay if the multiplier was negative.
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "<MultiplierHandler " + multiplier + ">";
+    }
+}
index 9283523275bb53f004e51b380b1497158b6b8a4e..b5fac7652617ec01bf9a6c28d3fee5c849c6e08a 100644 (file)
@@ -230,11 +230,10 @@ public class NumberParserImpl {
                     || properties.getMaximumFractionDigits() != 0;
             parser.addMatcher(RequireDecimalSeparatorValidator.getInstance(patternHasDecimalSeparator));
         }
+        // NOTE: Don't look at magnitude multiplier here. That is performed when percent sign is seen.
         if (properties.getMultiplier() != null) {
-            // We need to use a math context in order to prevent non-terminating decimal expansions.
-            // This is only used when dividing by the multiplier.
-            parser.addMatcher(new MultiplierHandler(properties.getMultiplier(),
-                    RoundingUtils.getMathContextOr34Digits(properties)));
+            parser.addMatcher(
+                    new MultiplierParseHandler(RoundingUtils.multiplierFromProperties(properties)));
         }
 
         parser.freeze();
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/Multiplier.java b/icu4j/main/classes/core/src/com/ibm/icu/number/Multiplier.java
new file mode 100644 (file)
index 0000000..d048221
--- /dev/null
@@ -0,0 +1,171 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.RoundingUtils;
+
+/**
+ * A class that defines a quantity by which a number should be multiplied when formatting.
+ *
+ * <p>
+ * To create a Multiplier, use one of the factory methods.
+ *
+ * @draft ICU 62
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberFormatter
+ */
+public class Multiplier {
+
+    private static final Multiplier DEFAULT = new Multiplier(0, null);
+    private static final Multiplier HUNDRED = new Multiplier(2, null);
+    private static final Multiplier THOUSAND = new Multiplier(3, null);
+
+    private static final BigDecimal BIG_DECIMAL_100 = BigDecimal.valueOf(100);
+    private static final BigDecimal BIG_DECIMAL_1000 = BigDecimal.valueOf(1000);
+
+    final int magnitude;
+    final BigDecimal arbitrary;
+    final BigDecimal reciprocal;
+    final MathContext mc;
+
+    private Multiplier(int magnitude, BigDecimal arbitrary) {
+        this(magnitude, arbitrary, RoundingUtils.DEFAULT_MATH_CONTEXT_34_DIGITS);
+    }
+
+    private Multiplier(int magnitude, BigDecimal arbitrary, MathContext mc) {
+        this.magnitude = magnitude;
+        this.arbitrary = arbitrary;
+        this.mc = mc;
+        // We need to use a math context in order to prevent non-terminating decimal expansions.
+        // This is only used when dividing by the multiplier.
+        if (arbitrary != null && BigDecimal.ZERO.compareTo(arbitrary) != 0) {
+            this.reciprocal = BigDecimal.ONE.divide(arbitrary, mc);
+        } else {
+            this.reciprocal = null;
+        }
+    }
+
+    /**
+     * Do not change the value of numbers when formatting or parsing.
+     *
+     * @return A Multiplier to prevent any multiplication.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Multiplier none() {
+        return DEFAULT;
+    }
+
+    /**
+     * Multiply numbers by 100 before formatting. Useful for combining with a percent unit:
+     * <p>
+     *
+     * <pre>
+     * NumberFormatter.with().unit(NoUnit.PERCENT).multiplier(Multiplier.powerOfTen(2))
+     * </pre>
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Multiplier powerOfTen(int power) {
+        if (power == 0) {
+            return DEFAULT;
+        } else if (power == 2) {
+            return HUNDRED;
+        } else if (power == 3) {
+            return THOUSAND;
+        } else {
+            return new Multiplier(power, null);
+        }
+    }
+
+    /**
+     * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions.
+     * <p>
+     * This method takes a BigDecimal; also see the version that takes a double.
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Multiplier arbitrary(BigDecimal multiplicand) {
+        if (multiplicand.compareTo(BigDecimal.ONE) == 0) {
+            return DEFAULT;
+        } else if (multiplicand.compareTo(BIG_DECIMAL_100) == 0) {
+            return HUNDRED;
+        } else if (multiplicand.compareTo(BIG_DECIMAL_1000) == 0) {
+            return THOUSAND;
+        } else {
+            return new Multiplier(0, multiplicand);
+        }
+    }
+
+    /**
+     * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions.
+     * <p>
+     * This method takes a double; also see the version that takes a BigDecimal.
+     *
+     * @return A Multiplier for passing to the setter in NumberFormatter.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberFormatter
+     */
+    public static Multiplier arbitrary(double multiplicand) {
+        if (multiplicand == 1) {
+            return DEFAULT;
+        } else if (multiplicand == 100.0) {
+            return HUNDRED;
+        } else if (multiplicand == 1000.0) {
+            return THOUSAND;
+        } else {
+            return new Multiplier(0, BigDecimal.valueOf(multiplicand));
+        }
+    }
+
+    /**
+     * @internal
+     * @deprecated ICU 62 This API is ICU internal only.
+     */
+    @Deprecated
+    public Multiplier withMathContext(MathContext mc) {
+        // TODO: Make this public?
+        if (this.mc.equals(mc)) {
+            return this;
+        }
+        return new Multiplier(magnitude, arbitrary, mc);
+    }
+
+    /**
+     * @internal
+     * @deprecated ICU 62 This API is ICU internal only.
+     */
+    @Deprecated
+    public void applyTo(DecimalQuantity quantity) {
+        quantity.adjustMagnitude(magnitude);
+        if (arbitrary != null) {
+            quantity.multiplyBy(arbitrary);
+        }
+    }
+
+    /**
+     * @internal
+     * @deprecated ICU 62 This API is ICU internal only.
+     */
+    @Deprecated
+    public void applyReciprocalTo(DecimalQuantity quantity) {
+        quantity.adjustMagnitude(-magnitude);
+        if (reciprocal != null) {
+            quantity.multiplyBy(reciprocal);
+            quantity.roundToMagnitude(quantity.getMagnitude() - mc.getPrecision(), mc);
+        }
+    }
+
+}
index e8d8d238b15d8599a14b8b9db7145c1ba01d5132..0ecca0e8138ff9239fbd2ad6584bb6172bee5726 100644 (file)
@@ -12,6 +12,7 @@ import com.ibm.icu.impl.number.LongNameHandler;
 import com.ibm.icu.impl.number.MacroProps;
 import com.ibm.icu.impl.number.MicroProps;
 import com.ibm.icu.impl.number.MicroPropsGenerator;
+import com.ibm.icu.impl.number.MultiplierFormatHandler;
 import com.ibm.icu.impl.number.MutablePatternModifier;
 import com.ibm.icu.impl.number.NumberStringBuilder;
 import com.ibm.icu.impl.number.Padder;
@@ -183,7 +184,7 @@ class NumberFormatterImpl {
 
         // Multiplier (compatibility mode value).
         if (macros.multiplier != null) {
-            chain = macros.multiplier.copyAndChain(chain);
+            chain = new MultiplierFormatHandler(macros.multiplier, chain);
         }
 
         // Rounding strategy
index 88033d70a3710e7e45bbc8fbfd4850cca9c0d132..5fb86d5a10c165f58f979c9ced8f9ed5ad4cc8bf 100644 (file)
@@ -39,9 +39,10 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
     static final int KEY_UNIT_WIDTH = 9;
     static final int KEY_SIGN = 10;
     static final int KEY_DECIMAL = 11;
-    static final int KEY_THRESHOLD = 12;
-    static final int KEY_PER_UNIT = 13;
-    static final int KEY_MAX = 14;
+    static final int KEY_MULTIPLIER = 12;
+    static final int KEY_THRESHOLD = 13;
+    static final int KEY_PER_UNIT = 14;
+    static final int KEY_MAX = 15;
 
     final NumberFormatterSettings<?> parent;
     final int key;
@@ -441,6 +442,36 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
         return create(KEY_DECIMAL, style);
     }
 
+    /**
+     * Sets a multiplier to be used to scale the number by an arbitrary amount before formatting. Most
+     * common values:
+     *
+     * <ul>
+     * <li>Multiply by 100: useful for percentages.
+     * <li>Multiply by an arbitrary value: useful for unit conversions.
+     * </ul>
+     *
+     * <p>
+     * Pass an element from a {@link Multiplier} factory method to this setter. For example:
+     *
+     * <pre>
+     * NumberFormatter.with().multiplier(Multiplier.powerOfTen(2))
+     * </pre>
+     *
+     * <p>
+     * The default is to not apply any multiplier.
+     *
+     * @param multiplier
+     *            An amount to be multiplied against numbers before formatting.
+     * @return The fluent chain
+     * @see Multiplier
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     */
+    public T multiplier(Multiplier multiplier) {
+        return create(KEY_MULTIPLIER, multiplier);
+    }
+
     /**
      * Internal method to set a starting macros.
      *
@@ -568,6 +599,11 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
                     macros.decimal = (DecimalSeparatorDisplay) current.value;
                 }
                 break;
+            case KEY_MULTIPLIER:
+                if (macros.multiplier == null) {
+                    macros.multiplier = (Multiplier) current.value;
+                }
+                break;
             case KEY_THRESHOLD:
                 if (macros.threshold == null) {
                     macros.threshold = (Long) current.value;
index 0d33059eda4435de442f6d36ed8efb18ea99da3c..385944f677d799a14c0a97709f75f6a2326950e3 100644 (file)
@@ -11,7 +11,6 @@ import com.ibm.icu.impl.number.CustomSymbolCurrency;
 import com.ibm.icu.impl.number.DecimalFormatProperties;
 import com.ibm.icu.impl.number.Grouper;
 import com.ibm.icu.impl.number.MacroProps;
-import com.ibm.icu.impl.number.MultiplierImpl;
 import com.ibm.icu.impl.number.Padder;
 import com.ibm.icu.impl.number.PatternStringParser;
 import com.ibm.icu.impl.number.PropertiesAffixPatternProvider;
@@ -299,11 +298,7 @@ final class NumberPropertyMapper {
         // MULTIPLIERS //
         /////////////////
 
-        if (properties.getMagnitudeMultiplier() != 0) {
-            macros.multiplier = new MultiplierImpl(properties.getMagnitudeMultiplier());
-        } else if (properties.getMultiplier() != null) {
-            macros.multiplier = new MultiplierImpl(properties.getMultiplier());
-        }
+        macros.multiplier = RoundingUtils.multiplierFromProperties(properties);
 
         //////////////////////
         // PROPERTY EXPORTS //
index e6138e280df933951babcd566deb5e13ef43d439..055bcc8f82d217811ecc4d930179b172d21b8a88 100644 (file)
@@ -11,6 +11,7 @@ import com.ibm.icu.impl.PatternProps;
 import com.ibm.icu.impl.SoftCache;
 import com.ibm.icu.impl.StringSegment;
 import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.impl.number.RoundingUtils;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
 import com.ibm.icu.number.NumberFormatter.GroupingStrategy;
 import com.ibm.icu.number.NumberFormatter.SignDisplay;
@@ -1267,7 +1268,7 @@ class NumberSkeletonImpl {
             }
 
             // Generate the options
-            if (macros.rounder.mathContext != Rounder.DEFAULT_MATH_CONTEXT) {
+            if (macros.rounder.mathContext != RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED) {
                 sb.append('/');
                 BlueprintHelpers.generateRoundingModeOption(macros.rounder.mathContext.getRoundingMode(),
                         sb);
index 47b6df70843ead9201143b5bc8fb0478b4b69b1f..d38f99e926b1b36f0846270dbcebe44d3de17963 100644 (file)
@@ -26,11 +26,8 @@ public abstract class Rounder implements Cloneable {
 
     /* package-private final */ MathContext mathContext;
 
-    /* package-private */ static final MathContext DEFAULT_MATH_CONTEXT = RoundingUtils
-            .mathContextUnlimited(RoundingUtils.DEFAULT_ROUNDING_MODE);
-
     /* package-private */ Rounder() {
-        mathContext = DEFAULT_MATH_CONTEXT;
+        mathContext = RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED;
     }
 
     /**
index b9ca3976f3086c8c4ea65052c3e75a910933545a..77291c929e08cea832b191815368796a0e8ba502 100644 (file)
@@ -29,6 +29,7 @@ import com.ibm.icu.number.CompactNotation;
 import com.ibm.icu.number.FractionRounder;
 import com.ibm.icu.number.IntegerWidth;
 import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.Multiplier;
 import com.ibm.icu.number.Notation;
 import com.ibm.icu.number.NumberFormatter;
 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
@@ -1882,6 +1883,84 @@ public class NumberFormatterApiTest {
                 "0.");
     }
 
+    @Test
+    public void multiplier() {
+        assertFormatDescending(
+                "Multiplier None",
+                null,
+                NumberFormatter.with().multiplier(Multiplier.none()),
+                ULocale.ENGLISH,
+                "87,650",
+                "8,765",
+                "876.5",
+                "87.65",
+                "8.765",
+                "0.8765",
+                "0.08765",
+                "0.008765",
+                "0");
+
+        assertFormatDescending(
+                "Multiplier Power of Ten",
+                null,
+                NumberFormatter.with().multiplier(Multiplier.powerOfTen(6)),
+                ULocale.ENGLISH,
+                "87,650,000,000",
+                "8,765,000,000",
+                "876,500,000",
+                "87,650,000",
+                "8,765,000",
+                "876,500",
+                "87,650",
+                "8,765",
+                "0");
+
+        assertFormatDescending(
+                "Multiplier Arbitrary Double",
+                null,
+                NumberFormatter.with().multiplier(Multiplier.arbitrary(5.2)),
+                ULocale.ENGLISH,
+                "455,780",
+                "45,578",
+                "4,557.8",
+                "455.78",
+                "45.578",
+                "4.5578",
+                "0.45578",
+                "0.045578",
+                "0");
+
+        assertFormatDescending(
+                "Multiplier Arbitrary BigDecimal",
+                null,
+                NumberFormatter.with().multiplier(Multiplier.arbitrary(new BigDecimal("5.2"))),
+                ULocale.ENGLISH,
+                "455,780",
+                "45,578",
+                "4,557.8",
+                "455.78",
+                "45.578",
+                "4.5578",
+                "0.45578",
+                "0.045578",
+                "0");
+
+        assertFormatDescending(
+                "Multiplier Zero",
+                null,
+                NumberFormatter.with().multiplier(Multiplier.arbitrary(0)),
+                ULocale.ENGLISH,
+                "0",
+                "0",
+                "0",
+                "0",
+                "0",
+                "0",
+                "0",
+                "0",
+                "0");
+    }
+
     @Test
     public void locale() {
         // Coverage for the locale setters.