mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 07:39:16 +00:00
ICU-20708 Fixing edge cases with negative infinity and NaN.
This commit is contained in:
parent
69fb255169
commit
1ef18dc761
33 changed files with 274 additions and 101 deletions
|
@ -736,7 +736,7 @@ CharString *Formattable::internalGetCharString(UErrorCode &status) {
|
|||
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)) {
|
||||
|
|
|
@ -272,15 +272,15 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode ro
|
|||
}
|
||||
|
||||
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::multiplyBy(const DecNum& multiplicand, UErrorCode& status)
|
|||
}
|
||||
|
||||
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 @@ bool DecimalQuantity::isNegative() const {
|
|||
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 @@ bool DecimalQuantity::isNaN() const {
|
|||
return (flags & NAN_FLAG) != 0;
|
||||
}
|
||||
|
||||
bool DecimalQuantity::isZero() const {
|
||||
bool DecimalQuantity::isZeroish() const {
|
||||
return precision == 0;
|
||||
}
|
||||
|
||||
|
@ -548,7 +554,10 @@ uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const {
|
|||
}
|
||||
|
||||
bool DecimalQuantity::fitsInLong(bool ignoreFraction) const {
|
||||
if (isZero()) {
|
||||
if (isInfinite() || isNaN()) {
|
||||
return false;
|
||||
}
|
||||
if (isZeroish()) {
|
||||
return true;
|
||||
}
|
||||
if (scale < 0 && !ignoreFraction) {
|
||||
|
|
|
@ -146,14 +146,17 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
|||
*/
|
||||
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;
|
||||
|
|
|
@ -697,7 +697,7 @@ void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, U
|
|||
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;
|
||||
|
|
|
@ -81,7 +81,7 @@ int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuant
|
|||
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 @@ MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErr
|
|||
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 @@ int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form
|
|||
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).
|
||||
|
|
|
@ -44,7 +44,7 @@ class NumberFormatterImpl : public UMemory {
|
|||
* @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 @@ class NumberFormatterImpl : public UMemory {
|
|||
/**
|
||||
* 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 @@ class NumberFormatterImpl : public UMemory {
|
|||
|
||||
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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -289,7 +289,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, 0, plural});
|
||||
fModifiers[i] = SimpleModifier(compiledFormatter, field, false, {this, SIGNUM_ZERO, plural});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,7 +306,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, 0, plural});
|
||||
fModifiers[i] = SimpleModifier(compoundCompiled, field, false, {this, SIGNUM_ZERO, plural});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,7 @@ void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &mic
|
|||
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];
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public
|
|||
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];
|
||||
|
|
|
@ -57,7 +57,7 @@ Modifier::Parameters::Parameters()
|
|||
: 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;
|
||||
|
|
|
@ -289,7 +289,7 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
|
|||
/**
|
||||
* 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 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
|
|||
* 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 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
|
|||
}
|
||||
|
||||
/** 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 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
|
|||
// 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);
|
||||
|
|
|
@ -43,7 +43,7 @@ void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols,
|
|||
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 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
|
|||
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 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
|
|||
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 @@ void ImmutablePatternModifier::applyToMicros(
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -48,7 +48,7 @@ class U_I18N_API ImmutablePatternModifier : public MicroPropsGenerator, public U
|
|||
|
||||
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 @@ class U_I18N_API MutablePatternModifier
|
|||
* 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 @@ class U_I18N_API MutablePatternModifier
|
|||
const PluralRules *fRules;
|
||||
|
||||
// Number details (initialized in setNumberProperties)
|
||||
int8_t fSignum;
|
||||
Signum fSignum;
|
||||
StandardPlural::Form fPlural;
|
||||
|
||||
// QuantityChain details (initialized in addToChain)
|
||||
|
|
|
@ -352,7 +352,7 @@ void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) {
|
|||
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 @@ PatternParser::patternInfoToProperties(DecimalFormatProperties& properties, Pars
|
|||
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 @@ PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFo
|
|||
}
|
||||
|
||||
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 @@ void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider&
|
|||
|
||||
// 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));
|
||||
|
||||
|
|
|
@ -295,7 +295,7 @@ class U_I18N_API PatternStringUtils {
|
|||
* 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);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig
|
|||
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 getDisplayMagnitudeFraction(int minFrac) {
|
|||
}
|
||||
|
||||
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 @@ bool RoundingImpl::isSignificantDigits() const {
|
|||
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 @@ RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl:
|
|||
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 @@ void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const
|
|||
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, UErrorCode& status) const
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -123,9 +123,15 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
|
|||
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);
|
||||
|
|
|
@ -91,6 +91,12 @@ enum CompactType {
|
|||
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 @@ class U_I18N_API Modifier {
|
|||
*/
|
||||
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 @@ class U_I18N_API ModifierStore {
|
|||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -281,7 +281,9 @@ void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patt
|
|||
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(
|
||||
|
|
|
@ -74,7 +74,7 @@ double ParsedNumber::getDouble(UErrorCode& status) const {
|
|||
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 @@ void ParsedNumber::populateFormattable(Formattable& output, parse_flags_t parseF
|
|||
}
|
||||
}
|
||||
U_ASSERT(!quantity.bogus);
|
||||
if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
|
||||
if (quantity.isZeroish() && quantity.isNegative() && !integerOnly) {
|
||||
output.setDouble(-0.0);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -344,7 +344,7 @@ typedef enum UNumberSignDisplay {
|
|||
|
||||
/**
|
||||
* 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 @@ typedef enum UNumberSignDisplay {
|
|||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -68,6 +68,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
|
|||
// TODO: Add this method if currency symbols override support is added.
|
||||
//void symbolsOverride();
|
||||
void sign();
|
||||
void signCoverage();
|
||||
void decimal();
|
||||
void scale();
|
||||
void locale();
|
||||
|
|
|
@ -86,6 +86,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
|
|||
// 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 @@ void NumberFormatterApiTest::notationScientific() {
|
|||
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 @@ void NumberFormatterApiTest::notationCompact() {
|
|||
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 @@ void NumberFormatterApiTest::sign() {
|
|||
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",
|
||||
|
|
|
@ -35,19 +35,19 @@ void PatternModifierTest::testBasic() {
|
|||
}
|
||||
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 @@ void PatternModifierTest::testBasic() {
|
|||
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 @@ void PatternModifierTest::testPatternWithNoPlaceholder() {
|
|||
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;
|
||||
|
|
|
@ -121,8 +121,11 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
|
|||
*/
|
||||
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();
|
||||
|
|
|
@ -183,7 +183,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
|
||||
@Override
|
||||
public void multiplyBy(BigDecimal multiplicand) {
|
||||
if (isInfinite() || isZero() || isNaN()) {
|
||||
if (isZeroish()) {
|
||||
return;
|
||||
}
|
||||
BigDecimal temp = toBigDecimal();
|
||||
|
@ -304,7 +304,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
|
||||
@Override
|
||||
public int signum() {
|
||||
return isNegative() ? -1 : isZero() ? 0 : 1;
|
||||
return isNegative() ? -1 : (isZeroish() && !isInfinite()) ? 0 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -318,7 +318,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isZero() {
|
||||
public boolean isZeroish() {
|
||||
return precision == 0;
|
||||
}
|
||||
|
||||
|
@ -398,8 +398,10 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
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 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
|||
* 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) {
|
||||
|
|
|
@ -153,7 +153,7 @@ public class ParsedNumber {
|
|||
}
|
||||
}
|
||||
assert quantity != null;
|
||||
if (quantity.isZero() && quantity.isNegative() && !integerOnly) {
|
||||
if (quantity.isZeroish() && quantity.isNegative() && !integerOnly) {
|
||||
return -0.0;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,14 +122,14 @@ public class CompactNotation extends Notation {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ public final class NumberFormatter {
|
|||
|
||||
/**
|
||||
* 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 @@ public final class NumberFormatter {
|
|||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -522,8 +522,8 @@ public abstract class Precision implements Cloneable {
|
|||
* @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 @@ public abstract class Precision implements Cloneable {
|
|||
apply(input);
|
||||
|
||||
// If the number rounded to zero, exit.
|
||||
if (input.isZero()) {
|
||||
if (input.isZeroish()) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
|
@ -603,7 +603,7 @@ public abstract class Precision implements Cloneable {
|
|||
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 @@ public abstract class Precision implements Cloneable {
|
|||
* compatibility mode.
|
||||
*/
|
||||
public void apply(DecimalQuantity quantity, int minInt) {
|
||||
assert quantity.isZero();
|
||||
assert quantity.isZeroish();
|
||||
quantity.setMinFraction(minSig - minInt);
|
||||
}
|
||||
}
|
||||
|
@ -744,7 +744,7 @@ public abstract class Precision implements Cloneable {
|
|||
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 @@ public abstract class Precision implements Cloneable {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ package com.ibm.icu.number;
|
|||
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 @@ public class ScientificNotation extends Notation implements Cloneable {
|
|||
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,
|
||||
|
|
|
@ -463,7 +463,7 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isZero() {
|
||||
public boolean isZeroish() {
|
||||
if (primary == -1) {
|
||||
return fallback.compareTo(BigDecimal.ZERO) == 0;
|
||||
} else {
|
||||
|
@ -518,7 +518,7 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
|
|||
|
||||
@Override
|
||||
public int signum() {
|
||||
return isNegative() ? -1 : isZero() ? 0 : 1;
|
||||
return isNegative() ? -1 : isZeroish() ? 0 : 1;
|
||||
}
|
||||
|
||||
private void setNegative(boolean isNegative) {
|
||||
|
|
|
@ -177,6 +177,22 @@ public class NumberFormatterApiTest {
|
|||
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 @@ public class NumberFormatterApiTest {
|
|||
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");
|
||||
|
@ -1996,6 +2028,36 @@ public class NumberFormatterApiTest {
|
|||
"-444,444.00 US dollars");
|
||||
}
|
||||
|
||||
@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(
|
||||
|
|
Loading…
Add table
Reference in a new issue