]> granicus.if.org Git - icu/commitdiff
ICU-13551 Adding EXCEPT_ZERO enum value to SignDisplay.
authorShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 02:49:07 +0000 (02:49 +0000)
committerShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 02:49:07 +0000 (02:49 +0000)
X-SVN-Rev: 40824

18 files changed:
icu4c/source/i18n/number_decimalquantity.cpp
icu4c/source/i18n/number_decimalquantity.h
icu4c/source/i18n/number_formatimpl.cpp
icu4c/source/i18n/number_modifiers.h
icu4c/source/i18n/number_patternmodifier.cpp
icu4c/source/i18n/number_patternmodifier.h
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4c/source/test/intltest/numbertest_patternmodifier.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/ParameterizedModifier.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java

index 72463576666bb11d77a83cd05241c6240b3213ce..6f6ac9def65823fbcb3813453c87e51642a68659 100644 (file)
@@ -265,6 +265,10 @@ bool DecimalQuantity::isNegative() const {
     return (flags & NEGATIVE_FLAG) != 0;
 }
 
+int8_t DecimalQuantity::signum() const {
+    return isNegative() ? -1 : isZero() ? 0 : 1;
+}
+
 bool DecimalQuantity::isInfinite() const {
     return (flags & INFINITY_FLAG) != 0;
 }
index ccb832623cb7bb6f123d0ca273cfb520fd759c50..3ff9fbeffef04ef3ea6edd495ff87cce5ec2a0c6 100644 (file)
@@ -115,6 +115,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
     /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
     bool isNegative() const;
 
+    /** @return -1 if the value is negative; 1 if positive; or 0 if zero. */
+    int8_t signum() const;
+
     /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
     bool isInfinite() const U_OVERRIDE;
 
index fcabdcd83c93d00ba2ceb807d0acbcea08f2cfb3..e2fc4f20beb85f4bd7ae2460702bbf1bdce45fde 100644 (file)
@@ -161,8 +161,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps &macros, bool safe,
     bool isPercent = isNoUnit && unitIsPercent(macros.unit);
     bool isPermille = isNoUnit && unitIsPermille(macros.unit);
     bool isCldrUnit = !isCurrency && !isNoUnit;
-    bool isAccounting =
-            macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS;
+    bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING
+            || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS
+            || macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
     CurrencyUnit currency(kDefaultCurrency, status);
     if (isCurrency) {
         currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
index 6a88828a44dd71bd01af4804efcbb745fe445d3d..962d17b574a76724f94260fcea0c3e47fb1f8423 100644 (file)
@@ -216,31 +216,33 @@ class U_I18N_API ParameterizedModifier : public UMemory {
         }
     }
 
-    void adoptPositiveNegativeModifiers(const Modifier *positive, const Modifier *negative) {
-        mods[0] = positive;
-        mods[1] = negative;
+    void adoptPositiveNegativeModifiers(
+            const Modifier *positive, const Modifier *zero, const Modifier *negative) {
+        mods[2] = positive;
+        mods[1] = zero;
+        mods[0] = negative;
     }
 
     /** The modifier is ADOPTED. */
-    void adoptSignPluralModifier(bool isNegative, StandardPlural::Form plural, const Modifier *mod) {
-        mods[getModIndex(isNegative, plural)] = mod;
+    void adoptSignPluralModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) {
+        mods[getModIndex(signum, plural)] = mod;
     }
 
     /** Returns a reference to the modifier; no ownership change. */
-    const Modifier *getModifier(bool isNegative) const {
-        return mods[isNegative ? 1 : 0];
+    const Modifier *getModifier(int8_t signum) const {
+        return mods[signum + 1];
     }
 
     /** Returns a reference to the modifier; no ownership change. */
-    const Modifier *getModifier(bool isNegative, StandardPlural::Form plural) const {
-        return mods[getModIndex(isNegative, plural)];
+    const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const {
+        return mods[getModIndex(signum, plural)];
     }
 
   private:
-    const Modifier *mods[2 * StandardPlural::COUNT];
+    const Modifier *mods[3 * StandardPlural::COUNT];
 
-    inline static int32_t getModIndex(bool isNegative, StandardPlural::Form plural) {
-        return static_cast<int32_t>(plural) * 2 + (isNegative ? 1 : 0);
+    inline static int32_t getModIndex(int8_t signum, StandardPlural::Form plural) {
+        return static_cast<int32_t>(plural) * 3 + (signum + 1);
     }
 };
 
index 0599f92a4f343bffcf914cc93f7c2f938a0b134b..0866285e4595e98a1732dd315a09bef1680700fd 100644 (file)
@@ -38,8 +38,8 @@ MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const Cu
     this->rules = rules;
 }
 
-void MutablePatternModifier::setNumberProperties(bool isNegative, StandardPlural::Form plural) {
-    this->isNegative = isNegative;
+void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) {
+    this->signum = signum;
     this->plural = plural;
 }
 
@@ -74,10 +74,12 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren
     if (needsPlurals()) {
         // Slower path when we require the plural keyword.
         for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
-            setNumberProperties(false, plural);
-            pm->adoptSignPluralModifier(false, plural, createConstantModifier(status));
-            setNumberProperties(true, plural);
-            pm->adoptSignPluralModifier(true, plural, createConstantModifier(status));
+            setNumberProperties(1, plural);
+            pm->adoptSignPluralModifier(1, plural, createConstantModifier(status));
+            setNumberProperties(0, plural);
+            pm->adoptSignPluralModifier(0, plural, createConstantModifier(status));
+            setNumberProperties(-1, plural);
+            pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status));
         }
         if (U_FAILURE(status)) {
             delete pm;
@@ -86,11 +88,13 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren
         return new ImmutablePatternModifier(pm, rules, parent);  // adopts pm
     } else {
         // Faster path when plural keyword is not needed.
-        setNumberProperties(false, StandardPlural::Form::COUNT);
+        setNumberProperties(1, StandardPlural::Form::COUNT);
         Modifier *positive = createConstantModifier(status);
-        setNumberProperties(true, StandardPlural::Form::COUNT);
+        setNumberProperties(0, StandardPlural::Form::COUNT);
+        Modifier *zero = createConstantModifier(status);
+        setNumberProperties(-1, StandardPlural::Form::COUNT);
         Modifier *negative = createConstantModifier(status);
-        pm->adoptPositiveNegativeModifiers(positive, negative);
+        pm->adoptPositiveNegativeModifiers(positive, zero, negative);
         if (U_FAILURE(status)) {
             delete pm;
             return nullptr;
@@ -123,13 +127,13 @@ void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroP
 
 void ImmutablePatternModifier::applyToMicros(MicroProps &micros, DecimalQuantity &quantity) const {
     if (rules == nullptr) {
-        micros.modMiddle = pm->getModifier(quantity.isNegative());
+        micros.modMiddle = pm->getModifier(quantity.signum());
     } else {
         // TODO: Fix this. Avoid the copy.
         DecimalQuantity copy(quantity);
         copy.roundToInfinity();
         StandardPlural::Form plural = copy.getStandardPlural(rules);
-        micros.modMiddle = pm->getModifier(quantity.isNegative(), plural);
+        micros.modMiddle = pm->getModifier(quantity.signum(), plural);
     }
 }
 
@@ -149,9 +153,9 @@ void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps &mi
         // TODO: Fix this. Avoid the copy.
         DecimalQuantity copy(fq);
         micros.rounding.apply(copy, status);
-        nonConstThis->setNumberProperties(fq.isNegative(), copy.getStandardPlural(rules));
+        nonConstThis->setNumberProperties(fq.signum(), copy.getStandardPlural(rules));
     } else {
-        nonConstThis->setNumberProperties(fq.isNegative(), StandardPlural::Form::COUNT);
+        nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT);
     }
     micros.modMiddle = this;
 }
@@ -278,14 +282,17 @@ void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) {
     inCharSequenceMode = true;
 
     // Should the output render '+' where '-' would normally appear in the pattern?
-    plusReplacesMinusSign = !isNegative && (
-            signDisplay == UNUM_SIGN_ALWAYS ||
-            signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS) &&
-                            patternInfo->positiveHasPlusSign() == false;
+    plusReplacesMinusSign = signum != -1
+            && (signDisplay == UNUM_SIGN_ALWAYS
+                    || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS
+                    || (signum == 1
+                            && (signDisplay == UNUM_SIGN_EXCEPT_ZERO
+                                    || signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO)))
+            && patternInfo->positiveHasPlusSign() == false;
 
     // Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.)
     bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && (
-            isNegative || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign));
+            signum == -1 || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign));
 
     // Resolve the flags for the affix pattern.
     fFlags = 0;
@@ -303,7 +310,7 @@ void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) {
     // Should we prepend a sign to the pattern?
     if (!isPrefix || useNegativeAffixPattern) {
         prependSign = false;
-    } else if (isNegative) {
+    } else if (signum == -1) {
         prependSign = signDisplay != UNUM_SIGN_NEVER;
     } else {
         prependSign = plusReplacesMinusSign;
index 3feaee04c8ce2e48abf11f1d0c38120b5e94b8c2..9c8b95f7764436c6fe28fd1a38c4baaae04eb265 100644 (file)
@@ -125,13 +125,13 @@ class U_I18N_API MutablePatternModifier
     /**
      * Sets attributes of the current number being processed.
      *
-     * @param isNegative
-     *            Whether the number is negative.
+     * @param signum
+     *            -1 if negative; +1 if positive; or 0 if zero.
      * @param plural
-     *            The plural form of the number, required only if the pattern contains the triple currency sign, "¤¤¤"
-     *            (and as indicated by {@link #needsPlurals()}).
+     *            The plural form of the number, required only if the pattern contains the triple
+     *            currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}).
      */
-    void setNumberProperties(bool isNegative, StandardPlural::Form plural);
+    void setNumberProperties(int8_t signum, StandardPlural::Form plural);
 
     /**
      * Returns true if the pattern represented by this MurkyModifier requires a plural keyword in order to localize.
@@ -211,7 +211,7 @@ class U_I18N_API MutablePatternModifier
     const PluralRules *rules;
 
     // Number details (initialized in setNumberProperties)
-    bool isNegative;
+    int8_t signum;
     StandardPlural::Form plural;
 
     // QuantityChain details (initialized in addToChain)
index 2215d096d7f3ce6bac2f1fd77db19323a597fa35..397181536b9ea978698c420930548692cb1cb72c 100644 (file)
@@ -190,21 +190,22 @@ typedef enum UNumberSignDisplay {
      *
      * @draft ICU 60
      */
-            UNUM_SIGN_AUTO,
+    UNUM_SIGN_AUTO,
 
     /**
-     * Show the minus sign on negative numbers and the plus sign on positive numbers.
+     * Show the minus sign on negative numbers and the plus sign on positive numbers, including zero.
+     * To hide the sign on zero, see {@link UNUM_SIGN_EXCEPT_ZERO}.
      *
      * @draft ICU 60
      */
-            UNUM_SIGN_ALWAYS,
+    UNUM_SIGN_ALWAYS,
 
     /**
      * Do not show the sign on positive or negative numbers.
      *
      * @draft ICU 60
      */
-            UNUM_SIGN_NEVER,
+    UNUM_SIGN_NEVER,
 
     /**
      * Use the locale-dependent accounting format on negative numbers, and do not show the sign on positive numbers.
@@ -220,22 +221,41 @@ typedef enum UNumberSignDisplay {
      *
      * @draft ICU 60
      */
-            UNUM_SIGN_ACCOUNTING,
+    UNUM_SIGN_ACCOUNTING,
 
     /**
-     * Use the locale-dependent accounting format on negative numbers, and show the plus sign on positive numbers.
-     * For more information on the accounting format, see the ACCOUNTING sign display strategy.
+     * Use the locale-dependent accounting format on negative numbers, and show the plus sign on
+     * positive numbers, including zero. For more information on the accounting format, see the
+     * ACCOUNTING sign display strategy. To hide the sign on zero, see
+     * {@link UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO}.
      *
      * @draft ICU 60
      */
-            UNUM_SIGN_ACCOUNTING_ALWAYS,
+    UNUM_SIGN_ACCOUNTING_ALWAYS,
+
+    /**
+     * Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
+     * sign on zero.
+     *
+     * @draft ICU 61
+     */
+    UNUM_SIGN_EXCEPT_ZERO,
+
+    /**
+     * Use the locale-dependent accounting format on negative numbers, and show the plus sign on
+     * positive numbers. Do not show a sign on zero. For more information on the accounting format,
+     * see the ACCOUNTING sign display strategy.
+     *
+     * @draft ICU 61
+     */
+    UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO,
 
     /**
      * One more than the highest UNumberSignDisplay value.
      *
      * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420.
      */
-            UNUM_SIGN_COUNT
+    UNUM_SIGN_COUNT
 } UNumberSignDisplay;
 
 /**
index 4054e2272719c226faa176b5f71ea286e189551a..1c1487372c33c4640c9d8aab75eaca38418884fc 100644 (file)
@@ -1380,6 +1380,13 @@ void NumberFormatterApiTest::sign() {
             -444444,
             u"-444,444");
 
+    assertFormatSingle(
+            u"Sign Auto Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
+            Locale::getEnglish(),
+            0,
+            u"0");
+
     assertFormatSingle(
             u"Sign Always Positive",
             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
@@ -1394,6 +1401,13 @@ void NumberFormatterApiTest::sign() {
             -444444,
             u"-444,444");
 
+    assertFormatSingle(
+            u"Sign Always Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
+            Locale::getEnglish(),
+            0,
+            u"+0");
+
     assertFormatSingle(
             u"Sign Never Positive",
             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
@@ -1408,6 +1422,13 @@ void NumberFormatterApiTest::sign() {
             -444444,
             u"444,444");
 
+    assertFormatSingle(
+            u"Sign Never Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
+            Locale::getEnglish(),
+            0,
+            u"0");
+
     assertFormatSingle(
             u"Sign Accounting Positive",
             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
@@ -1422,6 +1443,13 @@ void NumberFormatterApiTest::sign() {
             -444444,
             u"($444,444.00)");
 
+    assertFormatSingle(
+            u"Sign Accounting Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
+            Locale::getEnglish(),
+            0,
+            u"$0.00");
+
     assertFormatSingle(
             u"Sign Accounting-Always Positive",
             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
@@ -1436,6 +1464,55 @@ void NumberFormatterApiTest::sign() {
             -444444,
             u"($444,444.00)");
 
+    assertFormatSingle(
+            u"Sign Accounting-Always Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
+            Locale::getEnglish(),
+            0,
+            u"+$0.00");
+
+    assertFormatSingle(
+            u"Sign Except-Zero Positive",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
+            Locale::getEnglish(),
+            444444,
+            u"+444,444");
+
+    assertFormatSingle(
+            u"Sign Except-Zero Negative",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
+            Locale::getEnglish(),
+            -444444,
+            u"-444,444");
+
+    assertFormatSingle(
+            u"Sign Except-Zero Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
+            Locale::getEnglish(),
+            0,
+            u"0");
+
+    assertFormatSingle(
+            u"Sign Accounting-Except-Zero Positive",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
+            Locale::getEnglish(),
+            444444,
+            u"+$444,444.00");
+
+    assertFormatSingle(
+            u"Sign Accounting-Except-Zero Negative",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
+            Locale::getEnglish(),
+            -444444,
+            u"($444,444.00)");
+
+    assertFormatSingle(
+            u"Sign Accounting-Except-Zero Zero",
+            NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
+            Locale::getEnglish(),
+            0,
+            u"$0.00");
+
     assertFormatSingle(
             u"Sign Accounting Negative Hidden",
             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
index f30203ea80c7e0f21f4dd20969d168ced23908be..2d0d9d75cdfaf9d88599aa4b699c0eab630053f5 100644 (file)
@@ -31,13 +31,19 @@ void PatternModifierTest::testBasic() {
     assertSuccess("Spot 2", status);
     mod.setSymbols(&symbols, currency, UNUM_UNIT_WIDTH_SHORT, nullptr);
 
-    mod.setNumberProperties(false, StandardPlural::Form::COUNT);
+    mod.setNumberProperties(1, StandardPlural::Form::COUNT);
     assertEquals("Pattern a0b", u"a", getPrefix(mod, status));
     assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
     mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false);
     assertEquals("Pattern a0b", u"+a", getPrefix(mod, status));
     assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
-    mod.setNumberProperties(true, StandardPlural::Form::COUNT);
+    mod.setNumberProperties(0, StandardPlural::Form::COUNT);
+    assertEquals("Pattern a0b", u"+a", getPrefix(mod, status));
+    assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
+    mod.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false);
+    assertEquals("Pattern a0b", u"a", getPrefix(mod, status));
+    assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
+    mod.setNumberProperties(-1, StandardPlural::Form::COUNT);
     assertEquals("Pattern a0b", u"-a", getPrefix(mod, status));
     assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
     mod.setPatternAttributes(UNUM_SIGN_NEVER, false);
@@ -50,20 +56,24 @@ void PatternModifierTest::testBasic() {
     assertSuccess("Spot 4", status);
     mod.setPatternInfo(&patternInfo2);
     mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
-    mod.setNumberProperties(false, StandardPlural::Form::COUNT);
+    mod.setNumberProperties(1, StandardPlural::Form::COUNT);
     assertEquals("Pattern a0b;c-0d", u"a", getPrefix(mod, status));
     assertEquals("Pattern a0b;c-0d", u"b", getSuffix(mod, status));
     mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false);
     assertEquals("Pattern a0b;c-0d", u"c+", getPrefix(mod, status));
     assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
-    mod.setNumberProperties(true, StandardPlural::Form::COUNT);
+    mod.setNumberProperties(0, StandardPlural::Form::COUNT);
+    assertEquals("Pattern a0b;c-0d", u"c+", getPrefix(mod, status));
+    assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
+    mod.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false);
+    assertEquals("Pattern a0b;c-0d", u"a", getPrefix(mod, status));
+    assertEquals("Pattern a0b;c-0d", u"b", getSuffix(mod, status));
+    mod.setNumberProperties(-1, StandardPlural::Form::COUNT);
     assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status));
     assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
     mod.setPatternAttributes(UNUM_SIGN_NEVER, false);
-    assertEquals(
-            "Pattern a0b;c-0d",
-            u"c-",
-            getPrefix(mod, status)); // TODO: What should this behavior be?
+    // TODO: What should this behavior be?
+    assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status));
     assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status));
     assertSuccess("Spot 5", status);
 }
index 4d34b38133f19401a8bc5d0d5f008f351cb25fef..1a5d6f73ad6ce38096cd82a97724e6c3d0a1b1de 100644 (file)
@@ -108,6 +108,9 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
     /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
     public boolean isNegative();
 
+    /** @return -1 if the value is negative; 1 if positive; or 0 if zero. */
+    public int signum();
+
     /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
     @Override
     public boolean isInfinite();
index a242d7eac00729b60ff94d81234b4d483115fb61..ebb73f05efb863bc0c662909b632915a154e6d83 100644 (file)
@@ -295,6 +295,11 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
         return (flags & NEGATIVE_FLAG) != 0;
     }
 
+    @Override
+    public int signum() {
+        return isNegative() ? -1 : isZero() ? 0 : 1;
+    }
+
     @Override
     public boolean isInfinite() {
         return (flags & INFINITY_FLAG) != 0;
index 9f8ea1fcd27709ab9c1492f9a5daa4b7990aab70..e3a2049bd5b97c92dc4f80bf9bd40a20c7c353e0 100644 (file)
@@ -48,7 +48,7 @@ public class MutablePatternModifier
     PluralRules rules;
 
     // Number details
-    boolean isNegative;
+    int signum;
     StandardPlural plural;
 
     // QuantityChain details
@@ -121,15 +121,15 @@ public class MutablePatternModifier
     /**
      * Sets attributes of the current number being processed.
      *
-     * @param isNegative
-     *            Whether the number is negative.
+     * @param signum
+     *            -1 if negative; +1 if positive; or 0 if zero.
      * @param plural
      *            The plural form of the number, required only if the pattern contains the triple
      *            currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}).
      */
-    public void setNumberProperties(boolean isNegative, StandardPlural plural) {
+    public void setNumberProperties(int signum, StandardPlural plural) {
         assert (plural != null) == needsPlurals();
-        this.isNegative = isNegative;
+        this.signum = signum;
         this.plural = plural;
     }
 
@@ -172,20 +172,24 @@ public class MutablePatternModifier
             // Slower path when we require the plural keyword.
             ParameterizedModifier pm = new ParameterizedModifier();
             for (StandardPlural plural : StandardPlural.VALUES) {
-                setNumberProperties(false, plural);
-                pm.setModifier(false, plural, createConstantModifier(a, b));
-                setNumberProperties(true, plural);
-                pm.setModifier(true, plural, createConstantModifier(a, b));
+                setNumberProperties(1, plural);
+                pm.setModifier(1, plural, createConstantModifier(a, b));
+                setNumberProperties(0, plural);
+                pm.setModifier(0, plural, createConstantModifier(a, b));
+                setNumberProperties(-1, plural);
+                pm.setModifier(-1, plural, createConstantModifier(a, b));
             }
             pm.freeze();
             return new ImmutablePatternModifier(pm, rules, parent);
         } else {
             // Faster path when plural keyword is not needed.
-            setNumberProperties(false, null);
+            setNumberProperties(1, null);
             Modifier positive = createConstantModifier(a, b);
-            setNumberProperties(true, null);
+            setNumberProperties(0, null);
+            Modifier zero = createConstantModifier(a, b);
+            setNumberProperties(-1, null);
             Modifier negative = createConstantModifier(a, b);
-            ParameterizedModifier pm = new ParameterizedModifier(positive, negative);
+            ParameterizedModifier pm = new ParameterizedModifier(positive, zero, negative);
             return new ImmutablePatternModifier(pm, null, parent);
         }
     }
@@ -236,13 +240,13 @@ public class MutablePatternModifier
 
         public void applyToMicros(MicroProps micros, DecimalQuantity quantity) {
             if (rules == null) {
-                micros.modMiddle = pm.getModifier(quantity.isNegative());
+                micros.modMiddle = pm.getModifier(quantity.signum());
             } else {
                 // TODO: Fix this. Avoid the copy.
                 DecimalQuantity copy = quantity.createCopy();
                 copy.roundToInfinity();
                 StandardPlural plural = copy.getStandardPlural(rules);
-                micros.modMiddle = pm.getModifier(quantity.isNegative(), plural);
+                micros.modMiddle = pm.getModifier(quantity.signum(), plural);
             }
         }
     }
@@ -260,9 +264,9 @@ public class MutablePatternModifier
             // TODO: Fix this. Avoid the copy.
             DecimalQuantity copy = fq.createCopy();
             micros.rounding.apply(copy);
-            setNumberProperties(fq.isNegative(), copy.getStandardPlural(rules));
+            setNumberProperties(fq.signum(), copy.getStandardPlural(rules));
         } else {
-            setNumberProperties(fq.isNegative(), null);
+            setNumberProperties(fq.signum(), null);
         }
         micros.modMiddle = this;
         return micros;
@@ -370,14 +374,18 @@ public class MutablePatternModifier
         inCharSequenceMode = true;
 
         // Should the output render '+' where '-' would normally appear in the pattern?
-        plusReplacesMinusSign = !isNegative
-                && (signDisplay == SignDisplay.ALWAYS || signDisplay == SignDisplay.ACCOUNTING_ALWAYS)
+        plusReplacesMinusSign = signum != -1
+                && (signDisplay == SignDisplay.ALWAYS
+                        || signDisplay == SignDisplay.ACCOUNTING_ALWAYS
+                        || (signum == 1
+                                && (signDisplay == SignDisplay.EXCEPT_ZERO
+                                        || signDisplay == SignDisplay.ACCOUNTING_EXCEPT_ZERO)))
                 && patternInfo.positiveHasPlusSign() == false;
 
         // Should we use the affix from the negative subpattern? (If not, we will use the positive
         // subpattern.)
         boolean useNegativeAffixPattern = patternInfo.hasNegativeSubpattern()
-                && (isNegative || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
+                && (signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
 
         // Resolve the flags for the affix pattern.
         flags = 0;
@@ -395,7 +403,7 @@ public class MutablePatternModifier
         // Should we prepend a sign to the pattern?
         if (!isPrefix || useNegativeAffixPattern) {
             prependSign = false;
-        } else if (isNegative) {
+        } else if (signum == -1) {
             prependSign = signDisplay != SignDisplay.NEVER;
         } else {
             prependSign = plusReplacesMinusSign;
index 3ccf5080490f9d0b1fcd3d0b10bf37e9efee60e1..a553be4f30efb628f806f3f4a758cf0224aee640 100644 (file)
@@ -10,6 +10,7 @@ import com.ibm.icu.impl.StandardPlural;
  */
 public class ParameterizedModifier {
     private final Modifier positive;
+    private final Modifier zero;
     private final Modifier negative;
     final Modifier[] mods;
     boolean frozen;
@@ -20,8 +21,9 @@ public class ParameterizedModifier {
      * <p>
      * If this constructor is used, a plural form CANNOT be passed to {@link #getModifier}.
      */
-    public ParameterizedModifier(Modifier positive, Modifier negative) {
+    public ParameterizedModifier(Modifier positive, Modifier zero, Modifier negative) {
         this.positive = positive;
+        this.zero = zero;
         this.negative = negative;
         this.mods = null;
         this.frozen = true;
@@ -36,33 +38,34 @@ public class ParameterizedModifier {
      */
     public ParameterizedModifier() {
         this.positive = null;
+        this.zero = null;
         this.negative = null;
-        this.mods = new Modifier[2 * StandardPlural.COUNT];
+        this.mods = new Modifier[3 * StandardPlural.COUNT];
         this.frozen = false;
     }
 
-    public void setModifier(boolean isNegative, StandardPlural plural, Modifier mod) {
+    public void setModifier(int signum, StandardPlural plural, Modifier mod) {
         assert !frozen;
-        mods[getModIndex(isNegative, plural)] = mod;
+        mods[getModIndex(signum, plural)] = mod;
     }
 
     public void freeze() {
         frozen = true;
     }
 
-    public Modifier getModifier(boolean isNegative) {
+    public Modifier getModifier(int signum) {
         assert frozen;
         assert mods == null;
-        return isNegative ? negative : positive;
+        return signum == 0 ? zero : signum < 0 ? negative : positive;
     }
 
-    public Modifier getModifier(boolean isNegative, StandardPlural plural) {
+    public Modifier getModifier(int signum, StandardPlural plural) {
         assert frozen;
         assert positive == null;
-        return mods[getModIndex(isNegative, plural)];
+        return mods[getModIndex(signum, plural)];
     }
 
-    private static int getModIndex(boolean isNegative, StandardPlural plural) {
-        return plural.ordinal() * 2 + (isNegative ? 1 : 0);
+    private static int getModIndex(int signum, StandardPlural plural) {
+        return plural.ordinal() * 3 + (signum + 1);
     }
 }
index 5aece9b81b763979f47ef5eeb9b940bc59e94a49..7a819b18aeeb2252ca91626cdefffc1c4a3c0fac 100644 (file)
@@ -191,7 +191,8 @@ public final class NumberFormatter {
         AUTO,
 
         /**
-         * Show the minus sign on negative numbers and the plus sign on positive numbers.
+         * Show the minus sign on negative numbers and the plus sign on positive numbers, including zero.
+         * To hide the sign on zero, see {@link #EXCEPT_ZERO}.
          *
          * @draft ICU 60
          * @provisional This API might change or be removed in a future release.
@@ -229,14 +230,36 @@ public final class NumberFormatter {
 
         /**
          * Use the locale-dependent accounting format on negative numbers, and show the plus sign on
-         * positive numbers. For more information on the accounting format, see the ACCOUNTING sign
-         * display strategy.
+         * positive numbers, including zero. For more information on the accounting format, see the
+         * ACCOUNTING sign display strategy. To hide the sign on zero, see
+         * {@link #ACCOUNTING_EXCEPT_ZERO}.
          *
          * @draft ICU 60
          * @provisional This API might change or be removed in a future release.
          * @see NumberFormatter
          */
         ACCOUNTING_ALWAYS,
+
+        /**
+         * Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
+         * sign on zero.
+         *
+         * @draft ICU 61
+         * @provisional This API might change or be removed in a future release.
+         * @see NumberFormatter
+         */
+        EXCEPT_ZERO,
+
+        /**
+         * Use the locale-dependent accounting format on negative numbers, and show the plus sign on
+         * positive numbers. Do not show a sign on zero. For more information on the accounting format,
+         * see the ACCOUNTING sign display strategy.
+         *
+         * @draft ICU 61
+         * @provisional This API might change or be removed in a future release.
+         * @see NumberFormatter
+         */
+        ACCOUNTING_EXCEPT_ZERO,
     }
 
     /**
index 0702398bfb210b0749e9ec4ad8f1c207facfd88f..d612ab27bf3d231b5cd627090df8dc3861a053c9 100644 (file)
@@ -115,7 +115,8 @@ class NumberFormatterImpl {
         boolean isPermille = isNoUnit && unitIsPermille(macros.unit);
         boolean isCldrUnit = !isCurrency && !isNoUnit;
         boolean isAccounting = macros.sign == SignDisplay.ACCOUNTING
-                || macros.sign == SignDisplay.ACCOUNTING_ALWAYS;
+                || macros.sign == SignDisplay.ACCOUNTING_ALWAYS
+                || macros.sign == SignDisplay.ACCOUNTING_EXCEPT_ZERO;
         Currency currency = isCurrency ? (Currency) macros.unit : DEFAULT_CURRENCY;
         UnitWidth unitWidth = UnitWidth.SHORT;
         if (macros.unitWidth != null) {
index d0da60bc3041551ea1c7e729af8d0316464f5c4a..b910a9ca739905cd8b7f04aa76e4170b29b6fe36 100644 (file)
@@ -503,6 +503,11 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
     return (flags & NEGATIVE_FLAG) != 0;
   }
 
+  @Override
+  public int signum() {
+      return isNegative() ? -1 : isZero() ? 0 : 1;
+  }
+
   private void setNegative(boolean isNegative) {
     flags = (flags & (~NEGATIVE_FLAG)) | (isNegative ? NEGATIVE_FLAG : 0);
   }
index 9622e8dd1aae4b27d9d56afaa9282f3876a9f720..7069073c14bc85dc7ebb027d9d11c365de753571 100644 (file)
@@ -32,13 +32,19 @@ public class MutablePatternModifierTest {
                 UnitWidth.SHORT,
                 null);
 
-        mod.setNumberProperties(false, null);
+        mod.setNumberProperties(1, null);
         assertEquals("a", getPrefix(mod));
         assertEquals("b", getSuffix(mod));
         mod.setPatternAttributes(SignDisplay.ALWAYS, false);
         assertEquals("+a", getPrefix(mod));
         assertEquals("b", getSuffix(mod));
-        mod.setNumberProperties(true, null);
+        mod.setNumberProperties(0, null);
+        assertEquals("+a", getPrefix(mod));
+        assertEquals("b", getSuffix(mod));
+        mod.setPatternAttributes(SignDisplay.EXCEPT_ZERO, false);
+        assertEquals("a", getPrefix(mod));
+        assertEquals("b", getSuffix(mod));
+        mod.setNumberProperties(-1, null);
         assertEquals("-a", getPrefix(mod));
         assertEquals("b", getSuffix(mod));
         mod.setPatternAttributes(SignDisplay.NEVER, false);
@@ -47,13 +53,19 @@ public class MutablePatternModifierTest {
 
         mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"));
         mod.setPatternAttributes(SignDisplay.AUTO, false);
-        mod.setNumberProperties(false, null);
+        mod.setNumberProperties(1, null);
         assertEquals("a", getPrefix(mod));
         assertEquals("b", getSuffix(mod));
         mod.setPatternAttributes(SignDisplay.ALWAYS, false);
         assertEquals("c+", getPrefix(mod));
         assertEquals("d", getSuffix(mod));
-        mod.setNumberProperties(true, null);
+        mod.setNumberProperties(0, null);
+        assertEquals("c+", getPrefix(mod));
+        assertEquals("d", getSuffix(mod));
+        mod.setPatternAttributes(SignDisplay.EXCEPT_ZERO, false);
+        assertEquals("a", getPrefix(mod));
+        assertEquals("b", getSuffix(mod));
+        mod.setNumberProperties(-1, null);
         assertEquals("c-", getPrefix(mod));
         assertEquals("d", getSuffix(mod));
         mod.setPatternAttributes(SignDisplay.NEVER, false);
index 0e6aaa12860e287a0cc5bf28ed977073d034e832..e117875264c88e3a6945411ff461e3f8d28c93f1 100644 (file)
@@ -1477,6 +1477,14 @@ public class NumberFormatterApiTest {
                 -444444,
                 "-444,444");
 
+        assertFormatSingle(
+                "Sign Auto Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.AUTO),
+                ULocale.ENGLISH,
+                0,
+                "0");
+
         assertFormatSingle(
                 "Sign Always Positive",
                 "sign=ALWAYS",
@@ -1493,6 +1501,14 @@ public class NumberFormatterApiTest {
                 -444444,
                 "-444,444");
 
+        assertFormatSingle(
+                "Sign Always Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.ALWAYS),
+                ULocale.ENGLISH,
+                0,
+                "+0");
+
         assertFormatSingle(
                 "Sign Never Positive",
                 "sign=NEVER",
@@ -1509,6 +1525,14 @@ public class NumberFormatterApiTest {
                 -444444,
                 "444,444");
 
+        assertFormatSingle(
+                "Sign Never Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.NEVER),
+                ULocale.ENGLISH,
+                0,
+                "0");
+
         assertFormatSingle(
                 "Sign Accounting Positive",
                 "$USD sign=ACCOUNTING",
@@ -1525,6 +1549,14 @@ public class NumberFormatterApiTest {
                 -444444,
                 "($444,444.00)");
 
+        assertFormatSingle(
+                "Sign Accounting Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
+                ULocale.ENGLISH,
+                0,
+                "$0.00");
+
         assertFormatSingle(
                 "Sign Accounting-Always Positive",
                 "$USD sign=ACCOUNTING_ALWAYS",
@@ -1541,6 +1573,62 @@ public class NumberFormatterApiTest {
                 -444444,
                 "($444,444.00)");
 
+        assertFormatSingle(
+                "Sign Accounting-Always Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
+                ULocale.ENGLISH,
+                0,
+                "+$0.00");
+
+        assertFormatSingle(
+                "Sign Except-Zero Positive",
+                "",
+                NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
+                ULocale.ENGLISH,
+                444444,
+                "+444,444");
+
+        assertFormatSingle(
+                "Sign Always Negative",
+                "",
+                NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
+                ULocale.ENGLISH,
+                -444444,
+                "-444,444");
+
+        assertFormatSingle(
+                "Sign Except-Zero Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
+                ULocale.ENGLISH,
+                0,
+                "0");
+
+        assertFormatSingle(
+                "Sign Accounting-Except-Zero Positive",
+                "$USD sign=ACCOUNTING_ALWAYS",
+                NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
+                ULocale.ENGLISH,
+                444444,
+                "+$444,444.00");
+
+        assertFormatSingle(
+                "Sign Accounting-Except-Zero Negative",
+                "$USD sign=ACCOUNTING_ALWAYS",
+                NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
+                ULocale.ENGLISH,
+                -444444,
+                "($444,444.00)");
+
+        assertFormatSingle(
+                "Sign Accounting-Except-Zero Zero",
+                "",
+                NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
+                ULocale.ENGLISH,
+                0,
+                "$0.00");
+
         assertFormatSingle(
                 "Sign Accounting Negative Hidden",
                 "$USD unit-width=HIDDEN sign=ACCOUNTING",