• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 // Allow implicit conversion from char16_t* to UnicodeString for this file:
9 // Helpful in toString methods and elsewhere.
10 #define UNISTR_FROM_STRING_EXPLICIT
11 
12 #include "number_mapper.h"
13 #include "number_patternstring.h"
14 #include "unicode/errorcode.h"
15 #include "number_utils.h"
16 
17 using namespace icu;
18 using namespace icu::number;
19 using namespace icu::number::impl;
20 
21 
create(const DecimalFormatProperties & properties,const DecimalFormatSymbols & symbols,DecimalFormatWarehouse & warehouse,UErrorCode & status)22 UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
23                                                         const DecimalFormatSymbols& symbols,
24                                                         DecimalFormatWarehouse& warehouse,
25                                                         UErrorCode& status) {
26     return NumberFormatter::with().macros(oldToNew(properties, symbols, warehouse, nullptr, status));
27 }
28 
create(const DecimalFormatProperties & properties,const DecimalFormatSymbols & symbols,DecimalFormatWarehouse & warehouse,DecimalFormatProperties & exportedProperties,UErrorCode & status)29 UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties,
30                                                         const DecimalFormatSymbols& symbols,
31                                                         DecimalFormatWarehouse& warehouse,
32                                                         DecimalFormatProperties& exportedProperties,
33                                                         UErrorCode& status) {
34     return NumberFormatter::with().macros(
35             oldToNew(
36                     properties, symbols, warehouse, &exportedProperties, status));
37 }
38 
oldToNew(const DecimalFormatProperties & properties,const DecimalFormatSymbols & symbols,DecimalFormatWarehouse & warehouse,DecimalFormatProperties * exportedProperties,UErrorCode & status)39 MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& properties,
40                                           const DecimalFormatSymbols& symbols,
41                                           DecimalFormatWarehouse& warehouse,
42                                           DecimalFormatProperties* exportedProperties,
43                                           UErrorCode& status) {
44     MacroProps macros;
45     Locale locale = symbols.getLocale();
46 
47     /////////////
48     // SYMBOLS //
49     /////////////
50 
51     macros.symbols.setTo(symbols);
52 
53     //////////////////
54     // PLURAL RULES //
55     //////////////////
56 
57     if (!properties.currencyPluralInfo.fPtr.isNull()) {
58         macros.rules = properties.currencyPluralInfo.fPtr->getPluralRules();
59     }
60 
61     /////////////
62     // AFFIXES //
63     /////////////
64 
65     warehouse.affixProvider.setTo(properties, status);
66     macros.affixProvider = &warehouse.affixProvider.get();
67 
68     ///////////
69     // UNITS //
70     ///////////
71 
72     bool useCurrency = (
73             !properties.currency.isNull() ||
74             !properties.currencyPluralInfo.fPtr.isNull() ||
75             !properties.currencyUsage.isNull() ||
76             warehouse.affixProvider.get().hasCurrencySign());
77     CurrencyUnit currency = resolveCurrency(properties, locale, status);
78     UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD);
79     if (useCurrency) {
80         // NOTE: Slicing is OK.
81         macros.unit = currency; // NOLINT
82     }
83 
84     ///////////////////////
85     // ROUNDING STRATEGY //
86     ///////////////////////
87 
88     int32_t maxInt = properties.maximumIntegerDigits;
89     int32_t minInt = properties.minimumIntegerDigits;
90     int32_t maxFrac = properties.maximumFractionDigits;
91     int32_t minFrac = properties.minimumFractionDigits;
92     int32_t minSig = properties.minimumSignificantDigits;
93     int32_t maxSig = properties.maximumSignificantDigits;
94     double roundingIncrement = properties.roundingIncrement;
95     // Not assigning directly to macros.roundingMode here: we change
96     // roundingMode if and when we also change macros.precision.
97     RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN);
98     bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
99     bool explicitMinMaxSig = minSig != -1 || maxSig != -1;
100     // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
101     // maxFrac was
102     // set (but not both) on a currency instance.
103     // NOTE: Increments are handled in "Precision.constructCurrency()".
104     if (useCurrency && (minFrac == -1 || maxFrac == -1)) {
105         int32_t digits = ucurr_getDefaultFractionDigitsForUsage(
106                 currency.getISOCurrency(), currencyUsage, &status);
107         if (minFrac == -1 && maxFrac == -1) {
108             minFrac = digits;
109             maxFrac = digits;
110         } else if (minFrac == -1) {
111             minFrac = std::min(maxFrac, digits);
112         } else /* if (maxFrac == -1) */ {
113             maxFrac = std::max(minFrac, digits);
114         }
115     }
116     // Validate min/max int/frac.
117     // For backwards compatibility, minimum overrides maximum if the two conflict.
118     if (minInt == 0 && maxFrac != 0) {
119         minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 0)) ? 1 : minFrac;
120         maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
121         minInt = 0;
122         maxInt = maxInt < 0 ? -1 : maxInt > kMaxIntFracSig ? -1 : maxInt;
123     } else {
124         // Force a digit before the decimal point.
125         minFrac = minFrac < 0 ? 0 : minFrac;
126         maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
127         minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt;
128         maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt;
129     }
130     Precision precision;
131     if (!properties.currencyUsage.isNull()) {
132         precision = Precision::constructCurrency(currencyUsage).withCurrency(currency);
133     } else if (roundingIncrement != 0.0) {
134         if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) {
135             precision = Precision::constructFraction(minFrac, maxFrac);
136         } else {
137             // Convert the double increment to an integer increment
138             precision = Precision::increment(roundingIncrement).withMinFraction(minFrac);
139         }
140     } else if (explicitMinMaxSig) {
141         minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig;
142         maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig
143                                                                           ? kMaxIntFracSig : maxSig;
144         precision = Precision::constructSignificant(minSig, maxSig);
145     } else if (explicitMinMaxFrac) {
146         precision = Precision::constructFraction(minFrac, maxFrac);
147     } else if (useCurrency) {
148         precision = Precision::constructCurrency(currencyUsage);
149     }
150     if (!precision.isBogus()) {
151         macros.roundingMode = roundingMode;
152         macros.precision = precision;
153     }
154 
155     ///////////////////
156     // INTEGER WIDTH //
157     ///////////////////
158 
159     macros.integerWidth = IntegerWidth(
160             static_cast<digits_t>(minInt),
161             static_cast<digits_t>(maxInt),
162             properties.formatFailIfMoreThanMaxDigits);
163 
164     ///////////////////////
165     // GROUPING STRATEGY //
166     ///////////////////////
167 
168     macros.grouper = Grouper::forProperties(properties);
169 
170     /////////////
171     // PADDING //
172     /////////////
173 
174     if (properties.formatWidth > 0) {
175         macros.padder = Padder::forProperties(properties);
176     }
177 
178     ///////////////////////////////
179     // DECIMAL MARK ALWAYS SHOWN //
180     ///////////////////////////////
181 
182     macros.decimal = properties.decimalSeparatorAlwaysShown ? UNUM_DECIMAL_SEPARATOR_ALWAYS
183                                                             : UNUM_DECIMAL_SEPARATOR_AUTO;
184 
185     ///////////////////////
186     // SIGN ALWAYS SHOWN //
187     ///////////////////////
188 
189     macros.sign = properties.signAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO;
190 
191     /////////////////////////
192     // SCIENTIFIC NOTATION //
193     /////////////////////////
194 
195     if (properties.minimumExponentDigits != -1) {
196         // Scientific notation is required.
197         // This whole section feels like a hack, but it is needed for regression tests.
198         // The mapping from property bag to scientific notation is nontrivial due to LDML rules.
199         if (maxInt > 8) {
200             // But #13110: The maximum of 8 digits has unknown origins and is not in the spec.
201             // If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8.
202             maxInt = minInt;
203             macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
204         } else if (maxInt > minInt && minInt > 1) {
205             // Bug #13289: if maxInt > minInt > 1, then minInt should be 1.
206             minInt = 1;
207             macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
208         }
209         int engineering = maxInt < 0 ? -1 : maxInt;
210         macros.notation = ScientificNotation(
211                 // Engineering interval:
212                 static_cast<int8_t>(engineering),
213                 // Enforce minimum integer digits (for patterns like "000.00E0"):
214                 (engineering == minInt),
215                 // Minimum exponent digits:
216                 static_cast<digits_t>(properties.minimumExponentDigits),
217                 // Exponent sign always shown:
218                 properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO);
219         // Scientific notation also involves overriding the rounding mode.
220         // TODO: Overriding here is a bit of a hack. Should this logic go earlier?
221         if (macros.precision.fType == Precision::PrecisionType::RND_FRACTION) {
222             // For the purposes of rounding, get the original min/max int/frac, since the local
223             // variables have been manipulated for display purposes.
224             int maxInt_ = properties.maximumIntegerDigits;
225             int minInt_ = properties.minimumIntegerDigits;
226             int minFrac_ = properties.minimumFractionDigits;
227             int maxFrac_ = properties.maximumFractionDigits;
228             if (minInt_ == 0 && maxFrac_ == 0) {
229                 // Patterns like "#E0" and "##E0", which mean no rounding!
230                 macros.precision = Precision::unlimited();
231             } else if (minInt_ == 0 && minFrac_ == 0) {
232                 // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
233                 macros.precision = Precision::constructSignificant(1, maxFrac_ + 1);
234             } else {
235                 int maxSig_ = minInt_ + maxFrac_;
236                 // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1.
237                 if (maxInt_ > minInt_ && minInt_ > 1) {
238                     minInt_ = 1;
239                 }
240                 int minSig_ = minInt_ + minFrac_;
241                 // To avoid regression, maxSig is not reset when minInt_ set to 1.
242                 // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
243                 macros.precision = Precision::constructSignificant(minSig_, maxSig_);
244             }
245             macros.roundingMode = roundingMode;
246         }
247     }
248 
249     //////////////////////
250     // COMPACT NOTATION //
251     //////////////////////
252 
253     if (!properties.compactStyle.isNull()) {
254         if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) {
255             macros.notation = Notation::compactLong();
256         } else {
257             macros.notation = Notation::compactShort();
258         }
259     }
260 
261     /////////////////
262     // MULTIPLIERS //
263     /////////////////
264 
265     macros.scale = scaleFromProperties(properties);
266 
267     //////////////////////
268     // PROPERTY EXPORTS //
269     //////////////////////
270 
271     if (exportedProperties != nullptr) {
272 
273         exportedProperties->currency = currency;
274         exportedProperties->roundingMode = roundingMode;
275         exportedProperties->minimumIntegerDigits = minInt;
276         exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt;
277 
278         Precision rounding_;
279         if (precision.fType == Precision::PrecisionType::RND_CURRENCY) {
280             rounding_ = precision.withCurrency(currency, status);
281         } else {
282             rounding_ = precision;
283         }
284         int minFrac_ = minFrac;
285         int maxFrac_ = maxFrac;
286         int minSig_ = minSig;
287         int maxSig_ = maxSig;
288         double increment_ = 0.0;
289         if (rounding_.fType == Precision::PrecisionType::RND_FRACTION) {
290             minFrac_ = rounding_.fUnion.fracSig.fMinFrac;
291             maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac;
292         } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT
293                 || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE
294                 || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) {
295             minFrac_ = rounding_.fUnion.increment.fMinFrac;
296             // If incrementRounding is used, maxFrac is set equal to minFrac
297             maxFrac_ = rounding_.fUnion.increment.fMinFrac;
298             // Convert the integer increment to a double
299             DecimalQuantity dq;
300             dq.setToLong(rounding_.fUnion.increment.fIncrement);
301             dq.adjustMagnitude(rounding_.fUnion.increment.fIncrementMagnitude);
302             increment_ = dq.toDouble();
303         } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) {
304             minSig_ = rounding_.fUnion.fracSig.fMinSig;
305             maxSig_ = rounding_.fUnion.fracSig.fMaxSig;
306         }
307 
308         exportedProperties->minimumFractionDigits = minFrac_;
309         exportedProperties->maximumFractionDigits = maxFrac_;
310         exportedProperties->minimumSignificantDigits = minSig_;
311         exportedProperties->maximumSignificantDigits = maxSig_;
312         exportedProperties->roundingIncrement = increment_;
313     }
314 
315     return macros;
316 }
317 
318 
setTo(const DecimalFormatProperties & properties,UErrorCode & status)319 void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode& status) {
320     fBogus = false;
321 
322     // There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
323     // explicit setters (setPositivePrefix and friends).  The way to resolve the settings is as follows:
324     //
325     // 1) If the explicit setting is present for the field, use it.
326     // 2) Otherwise, follows UTS 35 rules based on the pattern string.
327     //
328     // Importantly, the explicit setters affect only the one field they override.  If you set the positive
329     // prefix, that should not affect the negative prefix.
330 
331     // Convenience: Extract the properties into local variables.
332     // Variables are named with three chars: [p/n][p/s][o/p]
333     // [p/n] => p for positive, n for negative
334     // [p/s] => p for prefix, s for suffix
335     // [o/p] => o for escaped custom override string, p for pattern string
336     UnicodeString ppo = AffixUtils::escape(properties.positivePrefix);
337     UnicodeString pso = AffixUtils::escape(properties.positiveSuffix);
338     UnicodeString npo = AffixUtils::escape(properties.negativePrefix);
339     UnicodeString nso = AffixUtils::escape(properties.negativeSuffix);
340     const UnicodeString& ppp = properties.positivePrefixPattern;
341     const UnicodeString& psp = properties.positiveSuffixPattern;
342     const UnicodeString& npp = properties.negativePrefixPattern;
343     const UnicodeString& nsp = properties.negativeSuffixPattern;
344 
345     if (!properties.positivePrefix.isBogus()) {
346         posPrefix = ppo;
347     } else if (!ppp.isBogus()) {
348         posPrefix = ppp;
349     } else {
350         // UTS 35: Default positive prefix is empty string.
351         posPrefix = u"";
352     }
353 
354     if (!properties.positiveSuffix.isBogus()) {
355         posSuffix = pso;
356     } else if (!psp.isBogus()) {
357         posSuffix = psp;
358     } else {
359         // UTS 35: Default positive suffix is empty string.
360         posSuffix = u"";
361     }
362 
363     if (!properties.negativePrefix.isBogus()) {
364         negPrefix = npo;
365     } else if (!npp.isBogus()) {
366         negPrefix = npp;
367     } else {
368         // UTS 35: Default negative prefix is "-" with positive prefix.
369         // Important: We prepend the "-" to the pattern, not the override!
370         negPrefix = ppp.isBogus() ? u"-" : u"-" + ppp;
371     }
372 
373     if (!properties.negativeSuffix.isBogus()) {
374         negSuffix = nso;
375     } else if (!nsp.isBogus()) {
376         negSuffix = nsp;
377     } else {
378         // UTS 35: Default negative prefix is the positive prefix.
379         negSuffix = psp.isBogus() ? u"" : psp;
380     }
381 
382     // For declaring if this is a currency pattern, we need to look at the
383     // original pattern, not at any user-specified overrides.
384     isCurrencyPattern = (
385         AffixUtils::hasCurrencySymbols(ppp, status) ||
386         AffixUtils::hasCurrencySymbols(psp, status) ||
387         AffixUtils::hasCurrencySymbols(npp, status) ||
388         AffixUtils::hasCurrencySymbols(nsp, status) ||
389         properties.currencyAsDecimal);
390 
391     fCurrencyAsDecimal = properties.currencyAsDecimal;
392 }
393 
charAt(int flags,int i) const394 char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const {
395     return getStringInternal(flags).charAt(i);
396 }
397 
length(int flags) const398 int PropertiesAffixPatternProvider::length(int flags) const {
399     return getStringInternal(flags).length();
400 }
401 
getString(int32_t flags) const402 UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const {
403     return getStringInternal(flags);
404 }
405 
getStringInternal(int32_t flags) const406 const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const {
407     bool prefix = (flags & AFFIX_PREFIX) != 0;
408     bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0;
409     if (prefix && negative) {
410         return negPrefix;
411     } else if (prefix) {
412         return posPrefix;
413     } else if (negative) {
414         return negSuffix;
415     } else {
416         return posSuffix;
417     }
418 }
419 
positiveHasPlusSign() const420 bool PropertiesAffixPatternProvider::positiveHasPlusSign() const {
421     // TODO: Change the internal APIs to propagate out the error?
422     ErrorCode localStatus;
423     return AffixUtils::containsType(posPrefix, TYPE_PLUS_SIGN, localStatus) ||
424            AffixUtils::containsType(posSuffix, TYPE_PLUS_SIGN, localStatus);
425 }
426 
hasNegativeSubpattern() const427 bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const {
428     return (
429         (negSuffix != posSuffix) ||
430         negPrefix.tempSubString(1) != posPrefix ||
431         negPrefix.charAt(0) != u'-'
432     );
433 }
434 
negativeHasMinusSign() const435 bool PropertiesAffixPatternProvider::negativeHasMinusSign() const {
436     ErrorCode localStatus;
437     return AffixUtils::containsType(negPrefix, TYPE_MINUS_SIGN, localStatus) ||
438            AffixUtils::containsType(negSuffix, TYPE_MINUS_SIGN, localStatus);
439 }
440 
hasCurrencySign() const441 bool PropertiesAffixPatternProvider::hasCurrencySign() const {
442     return isCurrencyPattern;
443 }
444 
containsSymbolType(AffixPatternType type,UErrorCode & status) const445 bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
446     return AffixUtils::containsType(posPrefix, type, status) ||
447            AffixUtils::containsType(posSuffix, type, status) ||
448            AffixUtils::containsType(negPrefix, type, status) ||
449            AffixUtils::containsType(negSuffix, type, status);
450 }
451 
hasBody() const452 bool PropertiesAffixPatternProvider::hasBody() const {
453     return true;
454 }
455 
currencyAsDecimal() const456 bool PropertiesAffixPatternProvider::currencyAsDecimal() const {
457     return fCurrencyAsDecimal;
458 }
459 
460 
setTo(const CurrencyPluralInfo & cpi,const DecimalFormatProperties & properties,UErrorCode & status)461 void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi,
462                                             const DecimalFormatProperties& properties,
463                                             UErrorCode& status) {
464     // We need to use a PropertiesAffixPatternProvider, not the simpler version ParsedPatternInfo,
465     // because user-specified affix overrides still need to work.
466     fBogus = false;
467     DecimalFormatProperties pluralProperties(properties);
468     for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) {
469         const char* keyword = StandardPlural::getKeyword(static_cast<StandardPlural::Form>(plural));
470         UnicodeString patternString;
471         patternString = cpi.getCurrencyPluralPattern(keyword, patternString);
472         PatternParser::parseToExistingProperties(
473                 patternString,
474                 pluralProperties,
475                 IGNORE_ROUNDING_NEVER,
476                 status);
477         affixesByPlural[plural].setTo(pluralProperties, status);
478     }
479 }
480 
charAt(int32_t flags,int32_t i) const481 char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const {
482     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
483     return affixesByPlural[pluralOrdinal].charAt(flags, i);
484 }
485 
length(int32_t flags) const486 int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const {
487     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
488     return affixesByPlural[pluralOrdinal].length(flags);
489 }
490 
getString(int32_t flags) const491 UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const {
492     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK);
493     return affixesByPlural[pluralOrdinal].getString(flags);
494 }
495 
positiveHasPlusSign() const496 bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const {
497     return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign();
498 }
499 
hasNegativeSubpattern() const500 bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const {
501     return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern();
502 }
503 
negativeHasMinusSign() const504 bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const {
505     return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign();
506 }
507 
hasCurrencySign() const508 bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const {
509     return affixesByPlural[StandardPlural::OTHER].hasCurrencySign();
510 }
511 
containsSymbolType(AffixPatternType type,UErrorCode & status) const512 bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const {
513     return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status);
514 }
515 
hasBody() const516 bool CurrencyPluralInfoAffixProvider::hasBody() const {
517     return affixesByPlural[StandardPlural::OTHER].hasBody();
518 }
519 
currencyAsDecimal() const520 bool CurrencyPluralInfoAffixProvider::currencyAsDecimal() const {
521     return affixesByPlural[StandardPlural::OTHER].currencyAsDecimal();
522 }
523 
524 
525 #endif /* #if !UCONFIG_NO_FORMATTING */
526