ICU-20709 Adding fourth signum type. Converting Java to use enum.
diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index 2c4c2ce..abbc23d 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp
@@ -319,10 +319,14 @@ bool DecimalQuantity::isNegative() const { } Signum DecimalQuantity::signum() const { - if (isNegative()) { + bool isZero = (isZeroish() && !isInfinite()); + bool isNeg = isNegative(); + if (isZero && isNeg) { + return SIGNUM_NEG_ZERO; + } else if (isZero) { + return SIGNUM_POS_ZERO; + } else if (isNeg) { return SIGNUM_NEG; - } else if (isZeroish() && !isInfinite()) { - return SIGNUM_ZERO; } else { return SIGNUM_POS; }
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp index 817aa0e..5519398 100644 --- a/icu4c/source/i18n/number_longnames.cpp +++ b/icu4c/source/i18n/number_longnames.cpp
@@ -308,7 +308,7 @@ void LongNameHandler::simpleFormatsToModifiers(const UnicodeString *simpleFormat if (U_FAILURE(status)) { return; } SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status); if (U_FAILURE(status)) { return; } - fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_ZERO, plural}); + fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_POS_ZERO, plural}); } } @@ -325,7 +325,7 @@ void LongNameHandler::multiSimpleFormatsToModifiers(const UnicodeString *leadFor if (U_FAILURE(status)) { return; } SimpleFormatter compoundCompiled(compoundFormat, 0, 1, status); if (U_FAILURE(status)) { return; } - fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_ZERO, plural}); + fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_POS_ZERO, plural}); } }
diff --git a/icu4c/source/i18n/number_modifiers.h b/icu4c/source/i18n/number_modifiers.h index c84c6aa..6fd0bad 100644 --- a/icu4c/source/i18n/number_modifiers.h +++ b/icu4c/source/i18n/number_modifiers.h
@@ -319,12 +319,12 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory { private: // NOTE: mods is zero-initialized (to nullptr) - const Modifier *mods[3 * StandardPlural::COUNT] = {}; + const Modifier *mods[4 * StandardPlural::COUNT] = {}; inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) { - U_ASSERT(signum >= -1 && signum <= 1); + U_ASSERT(signum >= 0 && signum <= 3); U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT); - return static_cast<int32_t>(plural) * 3 + (signum + 1); + return static_cast<int32_t>(plural) * 4 + signum; } };
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp index 724f5b9..3599ba7 100644 --- a/icu4c/source/i18n/number_patternmodifier.cpp +++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -81,8 +81,10 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) { setNumberProperties(SIGNUM_POS, plural); pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status)); - setNumberProperties(SIGNUM_ZERO, plural); - pm->adoptModifier(SIGNUM_ZERO, plural, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG_ZERO, plural); + pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status)); + setNumberProperties(SIGNUM_POS_ZERO, plural); + pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status)); setNumberProperties(SIGNUM_NEG, plural); pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status)); } @@ -95,8 +97,10 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren // Faster path when plural keyword is not needed. setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT); pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status)); - setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT); - pm->adoptModifierWithoutPlural(SIGNUM_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT); + pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status)); + setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT); + pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status)); setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT); pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status)); if (U_FAILURE(status)) { @@ -263,7 +267,12 @@ int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int pos /** This method contains the heart of the logic for rendering LDML affix strings. */ void MutablePatternModifier::prepareAffix(bool isPrefix) { PatternStringUtils::patternInfoToStringBuilder( - *fPatternInfo, isPrefix, fSignum, fSignDisplay, fPlural, fPerMilleReplacesPercent, currentAffix); + *fPatternInfo, + isPrefix, + PatternStringUtils::resolveSignDisplay(fSignDisplay, fSignum), + fPlural, + fPerMilleReplacesPercent, + currentAffix); } UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
diff --git a/icu4c/source/i18n/number_patternstring.cpp b/icu4c/source/i18n/number_patternstring.cpp index c7212c1..b2e6edc 100644 --- a/icu4c/source/i18n/number_patternstring.cpp +++ b/icu4c/source/i18n/number_patternstring.cpp
@@ -1000,23 +1000,19 @@ PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFo } void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix, - Signum signum, UNumberSignDisplay signDisplay, + PatternSignType patternSignType, StandardPlural::Form plural, bool perMilleReplacesPercent, UnicodeString& output) { // Should the output render '+' where '-' would normally appear in the pattern? - bool 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; + bool plusReplacesMinusSign = (patternSignType == PATTERN_SIGN_TYPE_POS_SIGN) + && !patternInfo.positiveHasPlusSign(); - // Should we use the affix from the negative subpattern? (If not, we will use the positive - // subpattern.) - // TODO: Deal with signum - bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && ( - signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); + // Should we use the affix from the negative subpattern? + // (If not, we will use the positive subpattern.) + bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() + && (patternSignType == PATTERN_SIGN_TYPE_NEG + || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); // Resolve the flags for the affix pattern. int flags = 0; @@ -1035,8 +1031,8 @@ void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& bool prependSign; if (!isPrefix || useNegativeAffixPattern) { prependSign = false; - } else if (signum == -1) { - prependSign = signDisplay != UNUM_SIGN_NEVER; + } else if (patternSignType == PATTERN_SIGN_TYPE_NEG) { + prependSign = true; } else { prependSign = plusReplacesMinusSign; } @@ -1065,4 +1061,54 @@ void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& } } +PatternSignType PatternStringUtils::resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum) { + switch (signDisplay) { + case UNUM_SIGN_AUTO: + case UNUM_SIGN_ACCOUNTING: + switch (signum) { + case SIGNUM_NEG: + case SIGNUM_NEG_ZERO: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_POS_ZERO: + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS; + } + break; + + case UNUM_SIGN_ALWAYS: + case UNUM_SIGN_ACCOUNTING_ALWAYS: + switch (signum) { + case SIGNUM_NEG: + case SIGNUM_NEG_ZERO: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_POS_ZERO: + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS_SIGN; + } + break; + + case UNUM_SIGN_EXCEPT_ZERO: + case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO: + switch (signum) { + case SIGNUM_NEG: + return PATTERN_SIGN_TYPE_NEG; + case SIGNUM_NEG_ZERO: + case SIGNUM_POS_ZERO: + return PATTERN_SIGN_TYPE_POS; + case SIGNUM_POS: + return PATTERN_SIGN_TYPE_POS_SIGN; + } + break; + + case UNUM_SIGN_NEVER: + return PATTERN_SIGN_TYPE_POS; + + default: + break; + } + + UPRV_UNREACHABLE; + return PATTERN_SIGN_TYPE_POS; +} + #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_patternstring.h b/icu4c/source/i18n/number_patternstring.h index 1191d29..54f37fd 100644 --- a/icu4c/source/i18n/number_patternstring.h +++ b/icu4c/source/i18n/number_patternstring.h
@@ -22,6 +22,18 @@ namespace impl { // Forward declaration class PatternParser; +// Note: the order of fields in this enum matters for parsing. +enum PatternSignType { + /** Render using normal positive subpattern rules */ + PATTERN_SIGN_TYPE_POS, + /** Render using rules to force the display of a plus sign */ + PATTERN_SIGN_TYPE_POS_SIGN, + /** Render using negative subpattern rules */ + PATTERN_SIGN_TYPE_NEG, + /** Count for looping over the possibilities */ + PATTERN_SIGN_TYPE_COUNT +}; + // Exported as U_I18N_API because it is a public member field of exported ParsedSubpatternInfo struct U_I18N_API Endpoints { int32_t start = 0; @@ -295,10 +307,12 @@ class U_I18N_API PatternStringUtils { * substitution, and plural forms for CurrencyPluralInfo. */ static void patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix, - Signum signum, UNumberSignDisplay signDisplay, + PatternSignType patternSignType, StandardPlural::Form plural, bool perMilleReplacesPercent, UnicodeString& output); + static PatternSignType resolveSignDisplay(UNumberSignDisplay signDisplay, Signum signum); + private: /** @return The number of chars inserted. */ static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex,
diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h index d62aa6a..49b77ee 100644 --- a/icu4c/source/i18n/number_types.h +++ b/icu4c/source/i18n/number_types.h
@@ -92,9 +92,10 @@ enum CompactType { }; enum Signum { - SIGNUM_NEG = -1, - SIGNUM_ZERO = 0, - SIGNUM_POS = 1 + SIGNUM_NEG = 0, + SIGNUM_NEG_ZERO = 1, + SIGNUM_POS_ZERO = 2, + SIGNUM_POS = 3 };
diff --git a/icu4c/source/i18n/numparse_affixes.cpp b/icu4c/source/i18n/numparse_affixes.cpp index cf8bab4..ca293e7 100644 --- a/icu4c/source/i18n/numparse_affixes.cpp +++ b/icu4c/source/i18n/numparse_affixes.cpp
@@ -271,8 +271,6 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt // Use initial capacity of 6, the highest possible number of AffixMatchers. UnicodeString sb; bool includeUnpaired = 0 != (parseFlags & PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES); - UNumberSignDisplay signDisplay = (0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) ? UNUM_SIGN_ALWAYS - : UNUM_SIGN_AUTO; int32_t numAffixMatchers = 0; int32_t numAffixPatternMatchers = 0; @@ -281,13 +279,23 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt AffixPatternMatcher* posSuffix = nullptr; // Pre-process the affix strings to resolve LDML rules like sign display. - for (int8_t signumInt = 1; signumInt >= -1; signumInt--) { - auto signum = static_cast<Signum>(signumInt); + for (int8_t typeInt = 0; typeInt < PATTERN_SIGN_TYPE_COUNT; typeInt++) { + auto type = static_cast<PatternSignType>(typeInt); + + // Skip affixes in some cases + if (type == PATTERN_SIGN_TYPE_POS + && 0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } + if (type == PATTERN_SIGN_TYPE_POS_SIGN + && 0 == (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } // Generate Prefix bool hasPrefix = false; PatternStringUtils::patternInfoToStringBuilder( - patternInfo, true, signum, signDisplay, StandardPlural::OTHER, false, sb); + patternInfo, true, type, StandardPlural::OTHER, false, sb); fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( sb, *fTokenWarehouse, parseFlags, &hasPrefix, status); AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++] @@ -296,13 +304,13 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt // Generate Suffix bool hasSuffix = false; PatternStringUtils::patternInfoToStringBuilder( - patternInfo, false, signum, signDisplay, StandardPlural::OTHER, false, sb); + patternInfo, false, type, StandardPlural::OTHER, false, sb); fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( sb, *fTokenWarehouse, parseFlags, &hasSuffix, status); AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++] : nullptr; - if (signum == 1) { + if (type == PATTERN_SIGN_TYPE_POS) { posPrefix = prefix; posSuffix = suffix; } else if (equals(prefix, posPrefix) && equals(suffix, posSuffix)) { @@ -311,17 +319,17 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt } // Flags for setting in the ParsedNumber; the token matchers may add more. - int flags = (signum == -1) ? FLAG_NEGATIVE : 0; + int flags = (type == PATTERN_SIGN_TYPE_NEG) ? FLAG_NEGATIVE : 0; // Note: it is indeed possible for posPrefix and posSuffix to both be null. // We still need to add that matcher for strict mode to work. fAffixMatchers[numAffixMatchers++] = {prefix, suffix, flags}; if (includeUnpaired && prefix != nullptr && suffix != nullptr) { // The following if statements are designed to prevent adding two identical matchers. - if (signum == 1 || !equals(prefix, posPrefix)) { + if (type == PATTERN_SIGN_TYPE_POS || !equals(prefix, posPrefix)) { fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; } - if (signum == 1 || !equals(suffix, posSuffix)) { + if (type == PATTERN_SIGN_TYPE_POS || !equals(suffix, posSuffix)) { fAffixMatchers[numAffixMatchers++] = {nullptr, suffix, flags}; } }
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index 4e4f0b7..726e7c5 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -2114,7 +2114,7 @@ void NumberFormatterApiTest::signCoverage() { { UNUM_SIGN_AUTO, { u"-∞", u"-1", u"-0", u"0", u"1", u"∞", u"NaN", u"-NaN" } }, { UNUM_SIGN_ALWAYS, { u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } }, { UNUM_SIGN_NEVER, { u"∞", u"1", u"0", u"0", u"1", u"∞", u"NaN", u"NaN" } }, - { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"-0", u"0", u"+1", u"+∞", u"NaN", u"-NaN" } }, + { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"0", u"0", u"+1", u"+∞", u"NaN", u"NaN" } }, }; double negNaN = std::copysign(uprv_getNaN(), -0.0); const double inputs[] = {
diff --git a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp index 8f61242..1d1e634 100644 --- a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp +++ b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
@@ -41,7 +41,10 @@ void PatternModifierTest::testBasic() { mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false); assertEquals("Pattern a0b", u"+a", getPrefix(mod, status)); assertEquals("Pattern a0b", u"b", getSuffix(mod, status)); - mod.setNumberProperties(SIGNUM_ZERO, StandardPlural::Form::COUNT); + mod.setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT); + assertEquals("Pattern a0b", u"-a", getPrefix(mod, status)); + assertEquals("Pattern a0b", u"b", getSuffix(mod, status)); + mod.setNumberProperties(SIGNUM_POS_ZERO, 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); @@ -66,7 +69,10 @@ void PatternModifierTest::testBasic() { 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(SIGNUM_ZERO, StandardPlural::Form::COUNT); + mod.setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT); + assertEquals("Pattern a0b;c-0d", u"c-", getPrefix(mod, status)); + assertEquals("Pattern a0b;c-0d", u"d", getSuffix(mod, status)); + mod.setNumberProperties(SIGNUM_POS_ZERO, 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); @@ -76,9 +82,8 @@ void PatternModifierTest::testBasic() { 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); - // 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)); + assertEquals("Pattern a0b;c-0d", u"a", getPrefix(mod, status)); + assertEquals("Pattern a0b;c-0d", u"b", getSuffix(mod, status)); assertSuccess("Spot 5", status); }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AdoptingModifierStore.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AdoptingModifierStore.java index 7e3459d..8365248 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AdoptingModifierStore.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AdoptingModifierStore.java
@@ -3,6 +3,7 @@ package com.ibm.icu.impl.number; import com.ibm.icu.impl.StandardPlural; +import com.ibm.icu.impl.number.Modifier.Signum; /** * This implementation of ModifierStore adopts references to Modifiers. @@ -11,7 +12,8 @@ */ public class AdoptingModifierStore implements ModifierStore { private final Modifier positive; - private final Modifier zero; + private final Modifier posZero; + private final Modifier negZero; private final Modifier negative; final Modifier[] mods; boolean frozen; @@ -22,9 +24,10 @@ public class AdoptingModifierStore implements ModifierStore { * <p> * If this constructor is used, a plural form CANNOT be passed to {@link #getModifier}. */ - public AdoptingModifierStore(Modifier positive, Modifier zero, Modifier negative) { + public AdoptingModifierStore(Modifier positive, Modifier posZero, Modifier negZero, Modifier negative) { this.positive = positive; - this.zero = zero; + this.posZero = posZero; + this.negZero = negZero; this.negative = negative; this.mods = null; this.frozen = true; @@ -39,13 +42,14 @@ public AdoptingModifierStore(Modifier positive, Modifier zero, Modifier negative */ public AdoptingModifierStore() { this.positive = null; - this.zero = null; + this.posZero = null; + this.negZero = null; this.negative = null; - this.mods = new Modifier[3 * StandardPlural.COUNT]; + this.mods = new Modifier[4 * StandardPlural.COUNT]; this.frozen = false; } - public void setModifier(int signum, StandardPlural plural, Modifier mod) { + public void setModifier(Signum signum, StandardPlural plural, Modifier mod) { assert !frozen; mods[getModIndex(signum, plural)] = mod; } @@ -54,21 +58,34 @@ public void freeze() { frozen = true; } - public Modifier getModifierWithoutPlural(int signum) { + public Modifier getModifierWithoutPlural(Signum signum) { assert frozen; assert mods == null; - return signum == 0 ? zero : signum < 0 ? negative : positive; + assert signum != null; + switch (signum) { + case POS: + return positive; + case POS_ZERO: + return posZero; + case NEG_ZERO: + return negZero; + case NEG: + return negative; + default: + throw new AssertionError("Unreachable"); + } } - public Modifier getModifier(int signum, StandardPlural plural) { + @Override + public Modifier getModifier(Signum signum, StandardPlural plural) { assert frozen; assert positive == null; return mods[getModIndex(signum, plural)]; } - private static int getModIndex(int signum, StandardPlural plural) { - assert signum >= -1 && signum <= 1; + private static int getModIndex(Signum signum, StandardPlural plural) { + assert signum != null; assert plural != null; - return plural.ordinal() * 3 + (signum + 1); + return plural.ordinal() * 4 + signum.ordinal(); } }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java index d551c32..7d30c58 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java
@@ -7,6 +7,7 @@ import java.text.FieldPosition; import com.ibm.icu.impl.StandardPlural; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.UFieldPosition; @@ -130,8 +131,8 @@ 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 The appropriate value from the Signum enum. */ + public Signum signum(); /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */ @Override
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java index badc530..09c8893 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
@@ -9,6 +9,7 @@ import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.Utility; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.PluralRules.Operand; import com.ibm.icu.text.UFieldPosition; @@ -303,8 +304,18 @@ public boolean isNegative() { } @Override - public int signum() { - return isNegative() ? -1 : (isZeroish() && !isInfinite()) ? 0 : 1; + public Signum signum() { + boolean isZero = (isZeroish() && !isInfinite()); + boolean isNeg = isNegative(); + if (isZero && isNeg) { + return Signum.NEG_ZERO; + } else if (isZero) { + return Signum.POS_ZERO; + } else if (isNeg) { + return Signum.NEG; + } else { + return Signum.POS; + } } @Override
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java index c0c6a74..459e514 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
@@ -12,6 +12,7 @@ import com.ibm.icu.impl.SimpleFormatterImpl; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.UResource; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.number.NumberFormatter.UnitWidth; import com.ibm.icu.text.NumberFormat; import com.ibm.icu.text.PluralRules; @@ -262,7 +263,7 @@ private void simpleFormatsToModifiers( String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 0, 1); Modifier.Parameters parameters = new Modifier.Parameters(); parameters.obj = this; - parameters.signum = 0; + parameters.signum = null;// Signum ignored parameters.plural = plural; modifiers.put(plural, new SimpleModifier(compiled, field, false, parameters)); } @@ -281,7 +282,7 @@ private void multiSimpleFormatsToModifiers( .compileToStringMinMaxArguments(compoundFormat, sb, 0, 1); Modifier.Parameters parameters = new Modifier.Parameters(); parameters.obj = this; - parameters.signum = 0; + parameters.signum = null; // Signum ignored parameters.plural = plural; modifiers.put(plural, new SimpleModifier(compoundCompiled, field, false, parameters)); } @@ -296,7 +297,8 @@ public MicroProps processQuantity(DecimalQuantity quantity) { } @Override - public Modifier getModifier(int signum, StandardPlural plural) { + public Modifier getModifier(Signum signum, StandardPlural plural) { + // Signum ignored return modifiers.get(plural); } }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java index cc56329..43542e1 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java
@@ -17,6 +17,13 @@ */ public interface Modifier { + static enum Signum { + NEG, + NEG_ZERO, + POS_ZERO, + POS + }; + /** * Apply this Modifier to the string builder. * @@ -65,7 +72,7 @@ public interface Modifier { */ public static class Parameters { public ModifierStore obj; - public int signum; + public Signum signum; public StandardPlural plural; }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierStore.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierStore.java index 1751c1c..2546cf2 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierStore.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierStore.java
@@ -3,6 +3,7 @@ package com.ibm.icu.impl.number; import com.ibm.icu.impl.StandardPlural; +import com.ibm.icu.impl.number.Modifier.Signum; /** * This is *not* a modifier; rather, it is an object that can return modifiers @@ -14,5 +15,5 @@ public interface ModifierStore { /** * Returns a Modifier with the given parameters (best-effort). */ - Modifier getModifier(int signum, StandardPlural plural); + Modifier getModifier(Signum signum, StandardPlural plural); }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java index 67f79b9..ebf30f6 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
@@ -50,7 +50,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr PluralRules rules; // Number details - int signum; + Signum signum; StandardPlural plural; // QuantityChain details @@ -129,7 +129,7 @@ public void setSymbols( * 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(int signum, StandardPlural plural) { + public void setNumberProperties(Signum signum, StandardPlural plural) { assert (plural != null) == needsPlurals(); this.signum = signum; this.plural = plural; @@ -174,24 +174,28 @@ public ImmutablePatternModifier createImmutableAndChain(MicroPropsGenerator pare // Slower path when we require the plural keyword. AdoptingModifierStore pm = new AdoptingModifierStore(); for (StandardPlural plural : StandardPlural.VALUES) { - 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)); + setNumberProperties(Signum.POS, plural); + pm.setModifier(Signum.POS, plural, createConstantModifier(a, b)); + setNumberProperties(Signum.POS_ZERO, plural); + pm.setModifier(Signum.POS_ZERO, plural, createConstantModifier(a, b)); + setNumberProperties(Signum.NEG_ZERO, plural); + pm.setModifier(Signum.NEG_ZERO, plural, createConstantModifier(a, b)); + setNumberProperties(Signum.NEG, plural); + pm.setModifier(Signum.NEG, plural, createConstantModifier(a, b)); } pm.freeze(); return new ImmutablePatternModifier(pm, rules, parent); } else { // Faster path when plural keyword is not needed. - setNumberProperties(1, null); + setNumberProperties(Signum.POS, null); Modifier positive = createConstantModifier(a, b); - setNumberProperties(0, null); - Modifier zero = createConstantModifier(a, b); - setNumberProperties(-1, null); + setNumberProperties(Signum.POS_ZERO, null); + Modifier posZero = createConstantModifier(a, b); + setNumberProperties(Signum.NEG_ZERO, null); + Modifier negZero = createConstantModifier(a, b); + setNumberProperties(Signum.NEG, null); Modifier negative = createConstantModifier(a, b); - AdoptingModifierStore pm = new AdoptingModifierStore(positive, zero, negative); + AdoptingModifierStore pm = new AdoptingModifierStore(positive, posZero, negZero, negative); return new ImmutablePatternModifier(pm, null, parent); } } @@ -367,8 +371,7 @@ private void prepareAffix(boolean isPrefix) { } PatternStringUtils.patternInfoToStringBuilder(patternInfo, isPrefix, - signum, - signDisplay, + PatternStringUtils.resolveSignDisplay(signDisplay, signum), plural, perMilleReplacesPercent, currentAffix);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java index 26eb457..601f1c8 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringUtils.java
@@ -5,6 +5,7 @@ import java.math.BigDecimal; import com.ibm.icu.impl.StandardPlural; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.impl.number.Padder.PadPosition; import com.ibm.icu.number.NumberFormatter.SignDisplay; import com.ibm.icu.text.DecimalFormatSymbols; @@ -14,6 +15,18 @@ */ public class PatternStringUtils { + // Note: the order of fields in this enum matters for parsing. + public static enum PatternSignType { + // Render using normal positive subpattern rules + POS, + // Render using rules to force the display of a plus sign + POS_SIGN, + // Render using negative subpattern rules + NEG; + + public static final PatternSignType[] VALUES = PatternSignType.values(); + }; + /** * Determine whether a given roundingIncrement should be ignored for formatting * based on the current maxFrac value (maximum fraction digits). For example a @@ -23,7 +36,7 @@ public class PatternStringUtils { * it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of * 0.005 is treated like 0.001 for significance). * - * This test is needed for both NumberPropertyMapper.oldToNew and + * This test is needed for both NumberPropertyMapper.oldToNew and * PatternStringUtils.propertiesToPatternString, but NumberPropertyMapper * is package-private so we have it here. * @@ -416,25 +429,19 @@ public static String convertLocalized( public static void patternInfoToStringBuilder( AffixPatternProvider patternInfo, boolean isPrefix, - int signum, - SignDisplay signDisplay, + PatternSignType patternSignType, StandardPlural plural, boolean perMilleReplacesPercent, StringBuilder output) { - // Should the output render '+' where '-' would normally appear in the pattern? - boolean plusReplacesMinusSign = signum != -1 - && (signDisplay == SignDisplay.ALWAYS - || signDisplay == SignDisplay.ACCOUNTING_ALWAYS - || (signum == 1 - && (signDisplay == SignDisplay.EXCEPT_ZERO - || signDisplay == SignDisplay.ACCOUNTING_EXCEPT_ZERO))) - && patternInfo.positiveHasPlusSign() == false; + boolean plusReplacesMinusSign = (patternSignType == PatternSignType.POS_SIGN) + && !patternInfo.positiveHasPlusSign(); - // Should we use the affix from the negative subpattern? (If not, we will use the positive - // subpattern.) + // Should we use the affix from the negative subpattern? + // (If not, we will use the positive subpattern.) boolean useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() - && (signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); + && (patternSignType == PatternSignType.NEG + || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); // Resolve the flags for the affix pattern. int flags = 0; @@ -453,8 +460,8 @@ public static void patternInfoToStringBuilder( boolean prependSign; if (!isPrefix || useNegativeAffixPattern) { prependSign = false; - } else if (signum == -1) { - prependSign = signDisplay != SignDisplay.NEVER; + } else if (patternSignType == PatternSignType.NEG) { + prependSign = true; } else { prependSign = plusReplacesMinusSign; } @@ -483,4 +490,53 @@ public static void patternInfoToStringBuilder( } } + public static PatternSignType resolveSignDisplay(SignDisplay signDisplay, Signum signum) { + switch (signDisplay) { + case AUTO: + case ACCOUNTING: + switch (signum) { + case NEG: + case NEG_ZERO: + return PatternSignType.NEG; + case POS_ZERO: + case POS: + return PatternSignType.POS; + } + break; + + case ALWAYS: + case ACCOUNTING_ALWAYS: + switch (signum) { + case NEG: + case NEG_ZERO: + return PatternSignType.NEG; + case POS_ZERO: + case POS: + return PatternSignType.POS_SIGN; + } + break; + + case EXCEPT_ZERO: + case ACCOUNTING_EXCEPT_ZERO: + switch (signum) { + case NEG: + return PatternSignType.NEG; + case NEG_ZERO: + case POS_ZERO: + return PatternSignType.POS; + case POS: + return PatternSignType.POS_SIGN; + } + break; + + case NEVER: + return PatternSignType.POS; + + default: + break; + } + + throw new AssertionError("Unreachable"); + } + }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java index 4292504..51b8df0 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java
@@ -12,7 +12,7 @@ import com.ibm.icu.impl.number.AffixPatternProvider; import com.ibm.icu.impl.number.AffixUtils; import com.ibm.icu.impl.number.PatternStringUtils; -import com.ibm.icu.number.NumberFormatter.SignDisplay; +import com.ibm.icu.impl.number.PatternStringUtils.PatternSignType; /** * @author sffc @@ -90,20 +90,27 @@ public static void createMatchers( StringBuilder sb = new StringBuilder(); ArrayList<AffixMatcher> matchers = new ArrayList<>(6); boolean includeUnpaired = 0 != (parseFlags & ParsingUtils.PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES); - SignDisplay signDisplay = (0 != (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED)) - ? SignDisplay.ALWAYS - : SignDisplay.AUTO; AffixPatternMatcher posPrefix = null; AffixPatternMatcher posSuffix = null; // Pre-process the affix strings to resolve LDML rules like sign display. - for (int signum = 1; signum >= -1; signum--) { + for (PatternSignType type : PatternSignType.VALUES) { + + // Skip affixes in some cases + if (type == PatternSignType.POS + && 0 != (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } + if (type == PatternSignType.POS_SIGN + && 0 == (parseFlags & ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED)) { + continue; + } + // Generate Prefix PatternStringUtils.patternInfoToStringBuilder(patternInfo, true, - signum, - signDisplay, + type, StandardPlural.OTHER, false, sb); @@ -113,15 +120,14 @@ public static void createMatchers( // Generate Suffix PatternStringUtils.patternInfoToStringBuilder(patternInfo, false, - signum, - signDisplay, + type, StandardPlural.OTHER, false, sb); AffixPatternMatcher suffix = AffixPatternMatcher .fromAffixPattern(sb.toString(), factory, parseFlags); - if (signum == 1) { + if (type == PatternSignType.POS) { posPrefix = prefix; posSuffix = suffix; } else if (Objects.equals(prefix, posPrefix) && Objects.equals(suffix, posSuffix)) { @@ -130,17 +136,17 @@ public static void createMatchers( } // Flags for setting in the ParsedNumber; the token matchers may add more. - int flags = (signum == -1) ? ParsedNumber.FLAG_NEGATIVE : 0; + int flags = (type == PatternSignType.NEG) ? ParsedNumber.FLAG_NEGATIVE : 0; // Note: it is indeed possible for posPrefix and posSuffix to both be null. // We still need to add that matcher for strict mode to work. matchers.add(getInstance(prefix, suffix, flags)); if (includeUnpaired && prefix != null && suffix != null) { // The following if statements are designed to prevent adding two identical matchers. - if (signum == 1 || !Objects.equals(prefix, posPrefix)) { + if (type == PatternSignType.POS || !Objects.equals(prefix, posPrefix)) { matchers.add(getInstance(prefix, null, flags)); } - if (signum == 1 || !Objects.equals(suffix, posSuffix)) { + if (type == PatternSignType.POS || !Objects.equals(suffix, posSuffix)) { matchers.add(getInstance(null, suffix, flags)); } }
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java index d8aae25..cb1bc57 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java
@@ -9,6 +9,7 @@ import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.number.DecimalQuantity; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.PluralRules.Operand; import com.ibm.icu.text.UFieldPosition; @@ -517,8 +518,18 @@ public boolean isNegative() { } @Override - public int signum() { - return isNegative() ? -1 : isZeroish() ? 0 : 1; + public Signum signum() { + boolean isZero = (isZeroish() && !isInfinite()); + boolean isNeg = isNegative(); + if (isZero && isNeg) { + return Signum.NEG_ZERO; + } else if (isZero) { + return Signum.POS_ZERO; + } else if (isNeg) { + return Signum.NEG; + } else { + return Signum.POS; + } } private void setNegative(boolean isNegative) {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java index 9f8585b..180919c 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java
@@ -12,6 +12,7 @@ import com.ibm.icu.impl.number.DecimalQuantity; import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD; import com.ibm.icu.impl.number.MicroProps; +import com.ibm.icu.impl.number.Modifier.Signum; import com.ibm.icu.impl.number.MutablePatternModifier; import com.ibm.icu.impl.number.PatternStringParser; import com.ibm.icu.number.NumberFormatter.SignDisplay; @@ -32,19 +33,22 @@ public void basic() { UnitWidth.SHORT, null); - mod.setNumberProperties(1, null); + mod.setNumberProperties(Signum.POS, null); assertEquals("a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); mod.setPatternAttributes(SignDisplay.ALWAYS, false); assertEquals("+a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); - mod.setNumberProperties(0, null); + mod.setNumberProperties(Signum.POS_ZERO, null); assertEquals("+a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); + mod.setNumberProperties(Signum.NEG_ZERO, 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); + mod.setNumberProperties(Signum.NEG, null); assertEquals("-a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); mod.setPatternAttributes(SignDisplay.NEVER, false); @@ -53,24 +57,27 @@ public void basic() { mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"), null); mod.setPatternAttributes(SignDisplay.AUTO, false); - mod.setNumberProperties(1, null); + mod.setNumberProperties(Signum.POS, null); assertEquals("a", getPrefix(mod)); assertEquals("b", getSuffix(mod)); mod.setPatternAttributes(SignDisplay.ALWAYS, false); assertEquals("c+", getPrefix(mod)); assertEquals("d", getSuffix(mod)); - mod.setNumberProperties(0, null); + mod.setNumberProperties(Signum.POS_ZERO, null); assertEquals("c+", getPrefix(mod)); assertEquals("d", getSuffix(mod)); + mod.setNumberProperties(Signum.NEG_ZERO, 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); + mod.setNumberProperties(Signum.NEG, null); assertEquals("c-", getPrefix(mod)); assertEquals("d", getSuffix(mod)); mod.setPatternAttributes(SignDisplay.NEVER, false); - assertEquals("c-", getPrefix(mod)); // TODO: What should this behavior be? - assertEquals("d", getSuffix(mod)); + assertEquals("a", getPrefix(mod)); + assertEquals("b", getSuffix(mod)); } @Test @@ -112,7 +119,7 @@ public void patternWithNoPlaceholder() { Currency.getInstance("USD"), UnitWidth.SHORT, null); - mod.setNumberProperties(1, null); + mod.setNumberProperties(Signum.POS_ZERO, null); // Unsafe Code Path FormattedStringBuilder nsb = new FormattedStringBuilder();
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java index e5a872c..dbbf464 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -2036,7 +2036,7 @@ public void signCoverage() { { {SignDisplay.AUTO}, { "-∞", "-1", "-0", "0", "1", "∞", "NaN", "-NaN" } }, { {SignDisplay.ALWAYS}, { "-∞", "-1", "-0", "+0", "+1", "+∞", "+NaN", "-NaN" } }, { {SignDisplay.NEVER}, { "∞", "1", "0", "0", "1", "∞", "NaN", "NaN" } }, - { {SignDisplay.EXCEPT_ZERO}, { "-∞", "-1", "-0", "0", "+1", "+∞", "NaN", "-NaN" } }, + { {SignDisplay.EXCEPT_ZERO}, { "-∞", "-1", "0", "0", "+1", "+∞", "NaN", "NaN" } }, }; double negNaN = Math.copySign(Double.NaN, -0.0); double inputs[] = new double[] {