ICU-20708 Fixing edge cases with negative infinity and NaN.
diff --git a/icu4c/source/i18n/fmtable.cpp b/icu4c/source/i18n/fmtable.cpp
index d813424..dbfd3c2 100644
--- a/icu4c/source/i18n/fmtable.cpp
+++ b/icu4c/source/i18n/fmtable.cpp
@@ -736,7 +736,7 @@
fDecimalStr->append("Infinity", status);
} else if (fDecimalQuantity->isNaN()) {
fDecimalStr->append("NaN", status);
- } else if (fDecimalQuantity->isZero()) {
+ } else if (fDecimalQuantity->isZeroish()) {
fDecimalStr->append("0", -1, status);
} else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
(fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
diff --git a/icu4c/source/i18n/number_compact.cpp b/icu4c/source/i18n/number_compact.cpp
index f330251..3d25999 100644
--- a/icu4c/source/i18n/number_compact.cpp
+++ b/icu4c/source/i18n/number_compact.cpp
@@ -272,15 +272,15 @@
parent->processQuantity(quantity, micros, status);
if (U_FAILURE(status)) { return; }
- // Treat zero as if it had magnitude 0
+ // Treat zero, NaN, and infinity as if they had magnitude 0
int32_t magnitude;
- if (quantity.isZero()) {
+ if (quantity.isZeroish()) {
magnitude = 0;
micros.rounder.apply(quantity, status);
} else {
// TODO: Revisit chooseMultiplierAndApply
int32_t multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status);
- magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
+ magnitude = quantity.isZeroish() ? 0 : quantity.getMagnitude();
magnitude -= multiplier;
}
diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp
index d899c27..2c4c2ce 100644
--- a/icu4c/source/i18n/number_decimalquantity.cpp
+++ b/icu4c/source/i18n/number_decimalquantity.cpp
@@ -205,7 +205,7 @@
}
void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) {
- if (isInfinite() || isZero() || isNaN()) {
+ if (isZeroish()) {
return;
}
// Convert to DecNum, multiply, and convert back.
@@ -218,7 +218,7 @@
}
void DecimalQuantity::divideBy(const DecNum& divisor, UErrorCode& status) {
- if (isInfinite() || isZero() || isNaN()) {
+ if (isZeroish()) {
return;
}
// Convert to DecNum, multiply, and convert back.
@@ -318,8 +318,14 @@
return (flags & NEGATIVE_FLAG) != 0;
}
-int8_t DecimalQuantity::signum() const {
- return isNegative() ? -1 : isZero() ? 0 : 1;
+Signum DecimalQuantity::signum() const {
+ if (isNegative()) {
+ return SIGNUM_NEG;
+ } else if (isZeroish() && !isInfinite()) {
+ return SIGNUM_ZERO;
+ } else {
+ return SIGNUM_POS;
+ }
}
bool DecimalQuantity::isInfinite() const {
@@ -330,7 +336,7 @@
return (flags & NAN_FLAG) != 0;
}
-bool DecimalQuantity::isZero() const {
+bool DecimalQuantity::isZeroish() const {
return precision == 0;
}
@@ -548,7 +554,10 @@
}
bool DecimalQuantity::fitsInLong(bool ignoreFraction) const {
- if (isZero()) {
+ if (isInfinite() || isNaN()) {
+ return false;
+ }
+ if (isZeroish()) {
return true;
}
if (scale < 0 && !ignoreFraction) {
diff --git a/icu4c/source/i18n/number_decimalquantity.h b/icu4c/source/i18n/number_decimalquantity.h
index 06cc836..4ec6c5a 100644
--- a/icu4c/source/i18n/number_decimalquantity.h
+++ b/icu4c/source/i18n/number_decimalquantity.h
@@ -146,14 +146,17 @@
*/
int32_t getMagnitude() const;
- /** @return Whether the value represented by this {@link DecimalQuantity} is zero. */
- bool isZero() const;
+ /**
+ * @return Whether the value represented by this {@link DecimalQuantity} is
+ * zero, infinity, or NaN.
+ */
+ bool isZeroish() const;
/** @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 The appropriate value from the Signum enum. */
+ Signum signum() const;
/** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
bool isInfinite() const U_OVERRIDE;
diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp
index bd49a12..2dbd2fa 100644
--- a/icu4c/source/i18n/number_fluent.cpp
+++ b/icu4c/source/i18n/number_fluent.cpp
@@ -697,7 +697,7 @@
void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result,
UErrorCode& status) const {
FormattedStringBuilder string;
- auto signum = static_cast<int8_t>(isNegative ? -1 : 1);
+ auto signum = static_cast<Signum>(isNegative ? SIGNUM_NEG : SIGNUM_POS);
// Always return affixes for plural form OTHER.
static const StandardPlural::Form plural = StandardPlural::OTHER;
int32_t prefixLength;
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 8741af3..fde97fb 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -81,7 +81,7 @@
return length;
}
-int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
+int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum,
StandardPlural::Form plural,
FormattedStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
@@ -129,7 +129,7 @@
return fMicros;
}
-int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural,
+int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural,
FormattedStringBuilder& outString, UErrorCode& status) const {
if (U_FAILURE(status)) { return 0; }
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
@@ -140,7 +140,7 @@
return modifier->getPrefixLength();
}
-int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
+int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,
FormattedStringBuilder& outString, UErrorCode& status) {
if (U_FAILURE(status)) { return 0; }
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
diff --git a/icu4c/source/i18n/number_formatimpl.h b/icu4c/source/i18n/number_formatimpl.h
index 44753c0..206c5f5 100644
--- a/icu4c/source/i18n/number_formatimpl.h
+++ b/icu4c/source/i18n/number_formatimpl.h
@@ -44,7 +44,7 @@
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
- static int32_t getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
+ static int32_t getPrefixSuffixStatic(const MacroProps& macros, Signum signum,
StandardPlural::Form plural, FormattedStringBuilder& outString,
UErrorCode& status);
@@ -61,7 +61,7 @@
/**
* Like getPrefixSuffixStatic() but uses the safe compiled object.
*/
- int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, FormattedStringBuilder& outString,
+ int32_t getPrefixSuffix(Signum signum, StandardPlural::Form plural, FormattedStringBuilder& outString,
UErrorCode& status) const;
const MicroProps& getRawMicroProps() const {
@@ -109,7 +109,7 @@
MicroProps& preProcessUnsafe(DecimalQuantity &inValue, UErrorCode &status);
- int32_t getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural,
+ int32_t getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,
FormattedStringBuilder& outString, UErrorCode& status);
/**
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp
index 0cd1600..bd41699 100644
--- a/icu4c/source/i18n/number_longnames.cpp
+++ b/icu4c/source/i18n/number_longnames.cpp
@@ -289,7 +289,7 @@
if (U_FAILURE(status)) { return; }
SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status);
if (U_FAILURE(status)) { return; }
- fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, 0, plural});
+ fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_ZERO, plural});
}
}
@@ -306,7 +306,7 @@
if (U_FAILURE(status)) { return; }
SimpleFormatter compoundCompiled(compoundFormat, 0, 1, status);
if (U_FAILURE(status)) { return; }
- fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, 0, plural});
+ fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_ZERO, plural});
}
}
@@ -317,7 +317,7 @@
micros.modOuter = &fModifiers[pluralForm];
}
-const Modifier* LongNameHandler::getModifier(int8_t /*signum*/, StandardPlural::Form plural) const {
+const Modifier* LongNameHandler::getModifier(Signum /*signum*/, StandardPlural::Form plural) const {
return &fModifiers[plural];
}
diff --git a/icu4c/source/i18n/number_longnames.h b/icu4c/source/i18n/number_longnames.h
index 76fb82d..38aca26 100644
--- a/icu4c/source/i18n/number_longnames.h
+++ b/icu4c/source/i18n/number_longnames.h
@@ -34,7 +34,7 @@
void
processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE;
- const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const U_OVERRIDE;
+ const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE;
private:
SimpleModifier fModifiers[StandardPlural::Form::COUNT];
diff --git a/icu4c/source/i18n/number_modifiers.cpp b/icu4c/source/i18n/number_modifiers.cpp
index aa6cf57..3a44f8f 100644
--- a/icu4c/source/i18n/number_modifiers.cpp
+++ b/icu4c/source/i18n/number_modifiers.cpp
@@ -57,7 +57,7 @@
: obj(nullptr) {}
Modifier::Parameters::Parameters(
- const ModifierStore* _obj, int8_t _signum, StandardPlural::Form _plural)
+ const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural)
: obj(_obj), signum(_signum), plural(_plural) {}
ModifierStore::~ModifierStore() = default;
diff --git a/icu4c/source/i18n/number_modifiers.h b/icu4c/source/i18n/number_modifiers.h
index 5c13c87..c84c6aa 100644
--- a/icu4c/source/i18n/number_modifiers.h
+++ b/icu4c/source/i18n/number_modifiers.h
@@ -289,7 +289,7 @@
/**
* Sets the Modifier with the specified signum and plural form.
*/
- void adoptModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) {
+ void adoptModifier(Signum signum, StandardPlural::Form plural, const Modifier *mod) {
U_ASSERT(mods[getModIndex(signum, plural)] == nullptr);
mods[getModIndex(signum, plural)] = mod;
}
@@ -298,13 +298,13 @@
* Sets the Modifier with the specified signum.
* The modifier will apply to all plural forms.
*/
- void adoptModifierWithoutPlural(int8_t signum, const Modifier *mod) {
+ void adoptModifierWithoutPlural(Signum signum, const Modifier *mod) {
U_ASSERT(mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] == nullptr);
mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod;
}
/** Returns a reference to the modifier; no ownership change. */
- const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const U_OVERRIDE {
+ const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE {
const Modifier* modifier = mods[getModIndex(signum, plural)];
if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) {
modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
@@ -313,7 +313,7 @@
}
/** Returns a reference to the modifier; no ownership change. */
- const Modifier *getModifierWithoutPlural(int8_t signum) const {
+ const Modifier *getModifierWithoutPlural(Signum signum) const {
return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
}
@@ -321,7 +321,7 @@
// NOTE: mods is zero-initialized (to nullptr)
const Modifier *mods[3 * StandardPlural::COUNT] = {};
- inline static int32_t getModIndex(int8_t signum, StandardPlural::Form plural) {
+ inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) {
U_ASSERT(signum >= -1 && signum <= 1);
U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT);
return static_cast<int32_t>(plural) * 3 + (signum + 1);
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp
index 8f3af2a..724f5b9 100644
--- a/icu4c/source/i18n/number_patternmodifier.cpp
+++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -43,7 +43,7 @@
fRules = rules;
}
-void MutablePatternModifier::setNumberProperties(int8_t signum, StandardPlural::Form plural) {
+void MutablePatternModifier::setNumberProperties(Signum signum, StandardPlural::Form plural) {
fSignum = signum;
fPlural = plural;
}
@@ -79,12 +79,12 @@
if (needsPlurals()) {
// Slower path when we require the plural keyword.
for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
- setNumberProperties(1, plural);
- pm->adoptModifier(1, plural, createConstantModifier(status));
- setNumberProperties(0, plural);
- pm->adoptModifier(0, plural, createConstantModifier(status));
- setNumberProperties(-1, plural);
- pm->adoptModifier(-1, plural, createConstantModifier(status));
+ 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, plural);
+ pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
}
if (U_FAILURE(status)) {
delete pm;
@@ -93,12 +93,12 @@
return new ImmutablePatternModifier(pm, fRules, parent); // adopts pm
} else {
// Faster path when plural keyword is not needed.
- setNumberProperties(1, StandardPlural::Form::COUNT);
- pm->adoptModifierWithoutPlural(1, createConstantModifier(status));
- setNumberProperties(0, StandardPlural::Form::COUNT);
- pm->adoptModifierWithoutPlural(0, createConstantModifier(status));
- setNumberProperties(-1, StandardPlural::Form::COUNT);
- pm->adoptModifierWithoutPlural(-1, createConstantModifier(status));
+ 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, StandardPlural::Form::COUNT);
+ pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
if (U_FAILURE(status)) {
delete pm;
return nullptr;
@@ -140,7 +140,7 @@
}
}
-const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const {
+const Modifier* ImmutablePatternModifier::getModifier(Signum signum, StandardPlural::Form plural) const {
if (rules == nullptr) {
return pm->getModifierWithoutPlural(signum);
} else {
diff --git a/icu4c/source/i18n/number_patternmodifier.h b/icu4c/source/i18n/number_patternmodifier.h
index 59ea02f..b2c90e0 100644
--- a/icu4c/source/i18n/number_patternmodifier.h
+++ b/icu4c/source/i18n/number_patternmodifier.h
@@ -48,7 +48,7 @@
void applyToMicros(MicroProps& micros, const DecimalQuantity& quantity, UErrorCode& status) const;
- const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const;
+ const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const;
private:
ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules,
@@ -142,7 +142,7 @@
* The plural form of the number, required only if the pattern contains the triple
* currency sign, "¤¤¤" (and as indicated by {@link #needsPlurals()}).
*/
- void setNumberProperties(int8_t signum, StandardPlural::Form plural);
+ void setNumberProperties(Signum signum, StandardPlural::Form plural);
/**
* Returns true if the pattern represented by this MurkyModifier requires a plural keyword in order to localize.
@@ -223,7 +223,7 @@
const PluralRules *fRules;
// Number details (initialized in setNumberProperties)
- int8_t fSignum;
+ Signum fSignum;
StandardPlural::Form fPlural;
// QuantityChain details (initialized in addToChain)
diff --git a/icu4c/source/i18n/number_patternstring.cpp b/icu4c/source/i18n/number_patternstring.cpp
index 9075424..c7212c1 100644
--- a/icu4c/source/i18n/number_patternstring.cpp
+++ b/icu4c/source/i18n/number_patternstring.cpp
@@ -352,7 +352,7 @@
result.groupingSizes += 1;
result.integerNumerals += 1;
result.integerTotal += 1;
- if (!result.rounding.isZero() || state.peek() != u'0') {
+ if (!result.rounding.isZeroish() || state.peek() != u'0') {
result.rounding.appendDigit(static_cast<int8_t>(state.peek() - u'0'), 0, true);
}
break;
@@ -532,7 +532,7 @@
properties.roundingIncrement = 0.0;
properties.minimumSignificantDigits = positive.integerAtSigns;
properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns;
- } else if (!positive.rounding.isZero()) {
+ } else if (!positive.rounding.isZeroish()) {
if (!ignoreRounding) {
properties.minimumFractionDigits = minFrac;
properties.maximumFractionDigits = positive.fractionTotal;
@@ -1000,7 +1000,7 @@
}
void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
- int8_t signum, UNumberSignDisplay signDisplay,
+ Signum signum, UNumberSignDisplay signDisplay,
StandardPlural::Form plural,
bool perMilleReplacesPercent, UnicodeString& output) {
@@ -1014,6 +1014,7 @@
// 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));
diff --git a/icu4c/source/i18n/number_patternstring.h b/icu4c/source/i18n/number_patternstring.h
index 0c90867..1191d29 100644
--- a/icu4c/source/i18n/number_patternstring.h
+++ b/icu4c/source/i18n/number_patternstring.h
@@ -295,7 +295,7 @@
* substitution, and plural forms for CurrencyPluralInfo.
*/
static void patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix,
- int8_t signum, UNumberSignDisplay signDisplay,
+ Signum signum, UNumberSignDisplay signDisplay,
StandardPlural::Form plural, bool perMilleReplacesPercent,
UnicodeString& output);
diff --git a/icu4c/source/i18n/number_rounding.cpp b/icu4c/source/i18n/number_rounding.cpp
index 9e369f7..813d4b6 100644
--- a/icu4c/source/i18n/number_rounding.cpp
+++ b/icu4c/source/i18n/number_rounding.cpp
@@ -33,7 +33,7 @@
if (maxSig == -1) {
return INT32_MIN;
}
- int magnitude = value.isZero() ? 0 : value.getMagnitude();
+ int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
return magnitude - maxSig + 1;
}
@@ -45,7 +45,7 @@
}
int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
- int magnitude = value.isZero() ? 0 : value.getMagnitude();
+ int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
return magnitude - minSig + 1;
}
@@ -306,8 +306,8 @@
int32_t
RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
UErrorCode &status) {
- // Do not call this method with zero.
- U_ASSERT(!input.isZero());
+ // Do not call this method with zero, NaN, or infinity.
+ U_ASSERT(!input.isZeroish());
// Perform the first attempt at rounding.
int magnitude = input.getMagnitude();
@@ -316,7 +316,7 @@
apply(input, status);
// If the number rounded to zero, exit.
- if (input.isZero() || U_FAILURE(status)) {
+ if (input.isZeroish() || U_FAILURE(status)) {
return multiplier;
}
@@ -374,7 +374,7 @@
value.setMinFraction(
uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)));
// Make sure that digits are displayed on zero.
- if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) {
+ if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
value.setMinInteger(1);
}
break;
@@ -436,7 +436,7 @@
void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
// This method is intended for the one specific purpose of helping print "00.000E0".
U_ASSERT(isSignificantDigits());
- U_ASSERT(value.isZero());
+ U_ASSERT(value.isZeroish());
value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
}
diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp
index 6fb4c19..f3de741 100644
--- a/icu4c/source/i18n/number_scientific.cpp
+++ b/icu4c/source/i18n/number_scientific.cpp
@@ -123,9 +123,15 @@
fParent->processQuantity(quantity, micros, status);
if (U_FAILURE(status)) { return; }
+ // Do not apply scientific notation to special doubles
+ if (quantity.isInfinite() || quantity.isNaN()) {
+ micros.modInner = µs.helpers.emptyStrongModifier;
+ return;
+ }
+
// Treat zero as if it had magnitude 0
int32_t exponent;
- if (quantity.isZero()) {
+ if (quantity.isZeroish()) {
if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
// Show "00.000E0" on pattern "00.000E0"
micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h
index 5aeead7..d62aa6a 100644
--- a/icu4c/source/i18n/number_types.h
+++ b/icu4c/source/i18n/number_types.h
@@ -91,6 +91,12 @@
TYPE_DECIMAL, TYPE_CURRENCY
};
+enum Signum {
+ SIGNUM_NEG = -1,
+ SIGNUM_ZERO = 0,
+ SIGNUM_POS = 1
+};
+
class U_I18N_API AffixPatternProvider {
public:
@@ -194,11 +200,11 @@
*/
struct U_I18N_API Parameters {
const ModifierStore* obj = nullptr;
- int8_t signum;
+ Signum signum;
StandardPlural::Form plural;
Parameters();
- Parameters(const ModifierStore* _obj, int8_t _signum, StandardPlural::Form _plural);
+ Parameters(const ModifierStore* _obj, Signum _signum, StandardPlural::Form _plural);
};
/**
@@ -229,7 +235,7 @@
/**
* Returns a Modifier with the given parameters (best-effort).
*/
- virtual const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const = 0;
+ virtual const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const = 0;
};
diff --git a/icu4c/source/i18n/numparse_affixes.cpp b/icu4c/source/i18n/numparse_affixes.cpp
index 6dc30d2..cf8bab4 100644
--- a/icu4c/source/i18n/numparse_affixes.cpp
+++ b/icu4c/source/i18n/numparse_affixes.cpp
@@ -281,7 +281,9 @@
AffixPatternMatcher* posSuffix = nullptr;
// Pre-process the affix strings to resolve LDML rules like sign display.
- for (int8_t signum = 1; signum >= -1; signum--) {
+ for (int8_t signumInt = 1; signumInt >= -1; signumInt--) {
+ auto signum = static_cast<Signum>(signumInt);
+
// Generate Prefix
bool hasPrefix = false;
PatternStringUtils::patternInfoToStringBuilder(
diff --git a/icu4c/source/i18n/numparse_parsednumber.cpp b/icu4c/source/i18n/numparse_parsednumber.cpp
index 6485d17..4b373a3 100644
--- a/icu4c/source/i18n/numparse_parsednumber.cpp
+++ b/icu4c/source/i18n/numparse_parsednumber.cpp
@@ -74,7 +74,7 @@
status = U_INVALID_STATE_ERROR;
return 0.0;
}
- if (quantity.isZero() && quantity.isNegative()) {
+ if (quantity.isZeroish() && quantity.isNegative()) {
return -0.0;
}
@@ -107,7 +107,7 @@
}
}
U_ASSERT(!quantity.bogus);
- if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
+ if (quantity.isZeroish() && quantity.isNegative() && !integerOnly) {
output.setDouble(-0.0);
return;
}
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index e4c21a4..b38763c 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -344,7 +344,7 @@
/**
* Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
- * sign on zero.
+ * sign on zero or NaN, unless the sign bit is set (-0.0 gets a sign).
*
* @draft ICU 61
*/
@@ -352,8 +352,9 @@
/**
* 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.
+ * positive numbers. Do not show a sign on zero or NaN, unless the sign bit is set (-0.0 gets a
+ * sign). For more information on the accounting format, see the ACCOUNTING sign display
+ * strategy.
*
* @draft ICU 61
*/
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index d139bc0..61a46e8 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -68,6 +68,7 @@
// TODO: Add this method if currency symbols override support is added.
//void symbolsOverride();
void sign();
+ void signCoverage();
void decimal();
void scale();
void locale();
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 8c448c9..cfc10ab 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -86,6 +86,7 @@
// TODO: Add this method if currency symbols override support is added.
//TESTCASE_AUTO(symbolsOverride);
TESTCASE_AUTO(sign);
+ TESTCASE_AUTO(signCoverage);
TESTCASE_AUTO(decimal);
TESTCASE_AUTO(scale);
TESTCASE_AUTO(locale);
@@ -216,6 +217,22 @@
Locale::getEnglish(),
-1000000,
u"-1E6");
+
+ assertFormatSingle(
+ u"Scientific Infinity",
+ u"scientific",
+ NumberFormatter::with().notation(Notation::scientific()),
+ Locale::getEnglish(),
+ -uprv_getInfinity(),
+ u"-∞");
+
+ assertFormatSingle(
+ u"Scientific NaN",
+ u"scientific",
+ NumberFormatter::with().notation(Notation::scientific()),
+ Locale::getEnglish(),
+ uprv_getNaN(),
+ u"NaN");
}
void NumberFormatterApiTest::notationCompact() {
@@ -432,6 +449,22 @@
1e7,
u"1000\u842C");
+ assertFormatSingle(
+ u"Compact Infinity",
+ u"compact-short",
+ NumberFormatter::with().notation(Notation::compactShort()),
+ Locale::getEnglish(),
+ -uprv_getInfinity(),
+ u"-∞");
+
+ assertFormatSingle(
+ u"Compact NaN",
+ u"compact-short",
+ NumberFormatter::with().notation(Notation::compactShort()),
+ Locale::getEnglish(),
+ uprv_getNaN(),
+ u"NaN");
+
// NOTE: There is no API for compact custom data in C++
// and thus no "Compact Somali No Figure" test
}
@@ -2069,6 +2102,39 @@
u"-444,444.00 US dollars");
}
+void NumberFormatterApiTest::signCoverage() {
+ // https://unicode-org.atlassian.net/browse/ICU-20708
+ IcuTestErrorCode status(*this, "signCoverage");
+ const struct TestCase {
+ UNumberSignDisplay sign;
+ const char16_t* expectedStrings[8];
+ } cases[] = {
+ { 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" } },
+ };
+ double negNaN = std::copysign(uprv_getNaN(), -0.0);
+ const double inputs[] = {
+ -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
+ };
+ for (auto& cas : cases) {
+ auto sign = cas.sign;
+ for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
+ auto input = inputs[i];
+ auto expected = cas.expectedStrings[i];
+ auto actual = NumberFormatter::with()
+ .sign(sign)
+ .locale(Locale::getUS())
+ .formatDouble(input, status)
+ .toString(status);
+ assertEquals(
+ DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
+ expected, actual);
+ }
+ }
+}
+
void NumberFormatterApiTest::decimal() {
assertFormatDescending(
u"Decimal Default",
diff --git a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
index e557dcf..8f61242 100644
--- a/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
+++ b/icu4c/source/test/intltest/numbertest_patternmodifier.cpp
@@ -35,19 +35,19 @@
}
mod.setSymbols(&symbols, ¤cySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
- mod.setNumberProperties(1, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_POS, 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(0, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_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);
assertEquals("Pattern a0b", u"a", getPrefix(mod, status));
assertEquals("Pattern a0b", u"b", getSuffix(mod, status));
- mod.setNumberProperties(-1, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_NEG, 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);
@@ -60,19 +60,19 @@
assertSuccess("Spot 4", status);
mod.setPatternInfo(&patternInfo2, UNUM_FIELD_COUNT);
mod.setPatternAttributes(UNUM_SIGN_AUTO, false);
- mod.setNumberProperties(1, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_POS, 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(0, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_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);
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);
+ mod.setNumberProperties(SIGNUM_NEG, 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);
@@ -96,7 +96,7 @@
return;
}
mod.setSymbols(&symbols, ¤cySymbols, UNUM_UNIT_WIDTH_SHORT, nullptr);
- mod.setNumberProperties(1, StandardPlural::Form::COUNT);
+ mod.setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
// Unsafe Code Path
FormattedStringBuilder nsb;
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 258274f..d551c32 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
@@ -121,8 +121,11 @@
*/
public int getMagnitude() throws ArithmeticException;
- /** @return Whether the value represented by this {@link DecimalQuantity} is zero. */
- public boolean isZero();
+ /**
+ * @return Whether the value represented by this {@link DecimalQuantity} is
+ * zero, infinity, or NaN.
+ */
+ public boolean isZeroish();
/** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
public boolean isNegative();
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 58a04c8..badc530 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
@@ -183,7 +183,7 @@
@Override
public void multiplyBy(BigDecimal multiplicand) {
- if (isInfinite() || isZero() || isNaN()) {
+ if (isZeroish()) {
return;
}
BigDecimal temp = toBigDecimal();
@@ -304,7 +304,7 @@
@Override
public int signum() {
- return isNegative() ? -1 : isZero() ? 0 : 1;
+ return isNegative() ? -1 : (isZeroish() && !isInfinite()) ? 0 : 1;
}
@Override
@@ -318,7 +318,7 @@
}
@Override
- public boolean isZero() {
+ public boolean isZeroish() {
return precision == 0;
}
@@ -398,8 +398,10 @@
public void setToDouble(double n) {
setBcdToZero();
flags = 0;
- // Double.compare() handles +0.0 vs -0.0
- if (Double.compare(n, 0.0) < 0) {
+ // The sign bit is the top bit in both double and long, so we can
+ // get the long bits for the double and compare it to zero to check
+ // the sign of the double.
+ if (Double.doubleToRawLongBits(n) < 0) {
flags |= NEGATIVE_FLAG;
n = -n;
}
@@ -619,7 +621,10 @@
* Returns whether or not a Long can fully represent the value stored in this DecimalQuantity.
*/
public boolean fitsInLong() {
- if (isZero()) {
+ if (isInfinite() || isNaN()) {
+ return false;
+ }
+ if (isZeroish()) {
return true;
}
if (scale < 0) {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java
index 315eac3..72f737f 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java
@@ -153,7 +153,7 @@
}
}
assert quantity != null;
- if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
+ if (quantity.isZeroish() && quantity.isNegative() && !integerOnly) {
return -0.0;
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
index 9502dc3..806f341 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/CompactNotation.java
@@ -122,14 +122,14 @@
MicroProps micros = parent.processQuantity(quantity);
assert micros.rounder != null;
- // Treat zero as if it had magnitude 0
+ // Treat zero, NaN, and infinity as if they had magnitude 0
int magnitude;
- if (quantity.isZero()) {
+ if (quantity.isZeroish()) {
magnitude = 0;
micros.rounder.apply(quantity);
} else {
int multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data);
- magnitude = quantity.isZero() ? 0 : quantity.getMagnitude();
+ magnitude = quantity.isZeroish() ? 0 : quantity.getMagnitude();
magnitude -= multiplier;
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
index 928055d..b109e85 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
@@ -346,7 +346,7 @@
/**
* Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
- * sign on zero.
+ * sign on zero or NaN, unless the sign bit is set (-0.0 gets a sign).
*
* @draft ICU 61
* @provisional This API might change or be removed in a future release.
@@ -356,8 +356,9 @@
/**
* 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.
+ * positive numbers. Do not show a sign on zero or NaN, unless the sign bit is set (-0.0 gets a
+ * sign). 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.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java b/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
index f239f9a..41795a2 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/Precision.java
@@ -522,8 +522,8 @@
* @return The number of orders of magnitude the input was adjusted by this method.
*/
int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) {
- // Do not call this method with zero.
- assert !input.isZero();
+ // Do not call this method with zero, NaN, or infinity.
+ assert !input.isZeroish();
// Perform the first attempt at rounding.
int magnitude = input.getMagnitude();
@@ -532,7 +532,7 @@
apply(input);
// If the number rounded to zero, exit.
- if (input.isZero()) {
+ if (input.isZeroish()) {
return multiplier;
}
@@ -603,7 +603,7 @@
value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext);
value.setMinFraction(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig)));
// Make sure that digits are displayed on zero.
- if (value.isZero() && minSig > 0) {
+ if (value.isZeroish() && minSig > 0) {
value.setMinInteger(1);
}
}
@@ -613,7 +613,7 @@
* compatibility mode.
*/
public void apply(DecimalQuantity quantity, int minInt) {
- assert quantity.isZero();
+ assert quantity.isZeroish();
quantity.setMinFraction(minSig - minInt);
}
}
@@ -744,7 +744,7 @@
if (maxSig == -1) {
return Integer.MIN_VALUE;
}
- int magnitude = value.isZero() ? 0 : value.getMagnitude();
+ int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
return magnitude - maxSig + 1;
}
@@ -756,7 +756,7 @@
}
private static int getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig) {
- int magnitude = value.isZero() ? 0 : value.getMagnitude();
+ int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
return magnitude - minSig + 1;
}
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java b/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
index f195f6e..8246af9 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/ScientificNotation.java
@@ -5,6 +5,7 @@
import java.text.Format.Field;
import com.ibm.icu.impl.FormattedStringBuilder;
+import com.ibm.icu.impl.number.ConstantAffixModifier;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.MicroPropsGenerator;
@@ -162,9 +163,15 @@
MicroProps micros = parent.processQuantity(quantity);
assert micros.rounder != null;
+ // Do not apply scientific notation to special doubles
+ if (quantity.isInfinite() || quantity.isNaN()) {
+ micros.modInner = ConstantAffixModifier.EMPTY;
+ return micros;
+ }
+
// Treat zero as if it had magnitude 0
int exponent;
- if (quantity.isZero()) {
+ if (quantity.isZeroish()) {
if (notation.requireMinInt && micros.rounder instanceof SignificantRounderImpl) {
// Show "00.000E0" on pattern "00.000E0"
((SignificantRounderImpl) micros.rounder).apply(quantity,
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 994ca99..d8aae25 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
@@ -463,7 +463,7 @@
}
@Override
- public boolean isZero() {
+ public boolean isZeroish() {
if (primary == -1) {
return fallback.compareTo(BigDecimal.ZERO) == 0;
} else {
@@ -518,7 +518,7 @@
@Override
public int signum() {
- return isNegative() ? -1 : isZero() ? 0 : 1;
+ return isNegative() ? -1 : isZeroish() ? 0 : 1;
}
private void setNegative(boolean isNegative) {
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 e0ad131..e09cd03 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
@@ -177,6 +177,22 @@
ULocale.ENGLISH,
-1000000,
"-1E6");
+
+ assertFormatSingle(
+ "Scientific Infinity",
+ "scientific",
+ NumberFormatter.with().notation(Notation.scientific()),
+ ULocale.ENGLISH,
+ Double.NEGATIVE_INFINITY,
+ "-∞");
+
+ assertFormatSingle(
+ "Scientific NaN",
+ "scientific",
+ NumberFormatter.with().notation(Notation.scientific()),
+ ULocale.ENGLISH,
+ Double.NaN,
+ "NaN");
}
@Test
@@ -386,6 +402,22 @@
1e7,
"1000\u842C");
+ assertFormatSingle(
+ "Compact Infinity",
+ "compact-short",
+ NumberFormatter.with().notation(Notation.compactShort()),
+ ULocale.ENGLISH,
+ Double.NEGATIVE_INFINITY,
+ "-∞");
+
+ assertFormatSingle(
+ "Compact NaN",
+ "compact-short",
+ NumberFormatter.with().notation(Notation.compactShort()),
+ ULocale.ENGLISH,
+ Double.NaN,
+ "NaN");
+
Map<String, Map<String, String>> compactCustomData = new HashMap<>();
Map<String, String> entry = new HashMap<>();
entry.put("one", "Kun");
@@ -1997,6 +2029,36 @@
}
@Test
+ public void signCoverage() {
+ // https://unicode-org.atlassian.net/browse/ICU-20708
+ Object[][][] cases = new Object[][][] {
+ { {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" } },
+ };
+ double negNaN = Math.copySign(Double.NaN, -0.0);
+ double inputs[] = new double[] {
+ Double.NEGATIVE_INFINITY, -1, -0.0, 0, 1, Double.POSITIVE_INFINITY, Double.NaN, negNaN
+ };
+ for (Object[][] cas : cases) {
+ SignDisplay sign = (SignDisplay) cas[0][0];
+ for (int i = 0; i < inputs.length; i++) {
+ double input = inputs[i];
+ String expected = (String) cas[1][i];
+ String actual = NumberFormatter.with()
+ .sign(sign)
+ .locale(Locale.US)
+ .format(input)
+ .toString();
+ assertEquals(
+ input + " " + sign,
+ expected, actual);
+ }
+ }
+ }
+
+ @Test
public void decimal() {
assertFormatDescending(
"Decimal Default",