1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
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
15 #include "unicode/errorcode.h"
16 #include "unicode/decimfmt.h"
17 #include "unicode/ucurr.h"
18 #include "unicode/ustring.h"
19 #include "number_decimalquantity.h"
20 #include "number_types.h"
21 #include "numparse_impl.h"
22 #include "number_mapper.h"
23 #include "number_patternstring.h"
25 #include "number_utils.h"
26 #include "number_utypes.h"
29 using namespace icu::number
;
30 using namespace icu::number::impl
;
31 using namespace icu::numparse
;
32 using namespace icu::numparse::impl
;
33 using ERoundingMode
= icu::DecimalFormat::ERoundingMode
;
34 using EPadPosition
= icu::DecimalFormat::EPadPosition
;
36 // MSVC VS2015 warns C4805 when comparing bool with UBool, VS2017 no longer emits this warning.
37 // TODO: Move this macro into a better place?
38 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
39 #define UBOOL_TO_BOOL(b) static_cast<bool>(b)
41 #define UBOOL_TO_BOOL(b) b
45 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat
)
48 DecimalFormat::DecimalFormat(UErrorCode
& status
)
49 : DecimalFormat(nullptr, status
) {
50 if (U_FAILURE(status
)) { return; }
51 // Use the default locale and decimal pattern.
52 const char* localeName
= Locale::getDefault().getName();
53 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(status
));
54 UnicodeString patternString
= utils::getPatternForStyle(
57 CLDR_PATTERN_STYLE_DECIMAL
,
59 setPropertiesFromPattern(patternString
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
63 DecimalFormat::DecimalFormat(const UnicodeString
& pattern
, UErrorCode
& status
)
64 : DecimalFormat(nullptr, status
) {
65 if (U_FAILURE(status
)) { return; }
66 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
70 DecimalFormat::DecimalFormat(const UnicodeString
& pattern
, DecimalFormatSymbols
* symbolsToAdopt
,
72 : DecimalFormat(symbolsToAdopt
, status
) {
73 if (U_FAILURE(status
)) { return; }
74 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
78 DecimalFormat::DecimalFormat(const UnicodeString
& pattern
, DecimalFormatSymbols
* symbolsToAdopt
,
79 UNumberFormatStyle style
, UErrorCode
& status
)
80 : DecimalFormat(symbolsToAdopt
, status
) {
81 if (U_FAILURE(status
)) { return; }
82 // If choice is a currency type, ignore the rounding information.
83 if (style
== UNumberFormatStyle::UNUM_CURRENCY
||
84 style
== UNumberFormatStyle::UNUM_CURRENCY_ISO
||
85 style
== UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING
||
86 style
== UNumberFormatStyle::UNUM_CASH_CURRENCY
||
87 style
== UNumberFormatStyle::UNUM_CURRENCY_STANDARD
||
88 style
== UNumberFormatStyle::UNUM_CURRENCY_PLURAL
) {
89 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_ALWAYS
, status
);
91 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
93 // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there,
94 // so we have to set it here.
95 if (style
== UNumberFormatStyle::UNUM_CURRENCY_PLURAL
) {
96 LocalPointer
<CurrencyPluralInfo
> cpi(
97 new CurrencyPluralInfo(fields
->symbols
->getLocale(), status
),
99 if (U_FAILURE(status
)) { return; }
100 fields
->properties
.currencyPluralInfo
.fPtr
.adoptInstead(cpi
.orphan());
105 DecimalFormat::DecimalFormat(const DecimalFormatSymbols
* symbolsToAdopt
, UErrorCode
& status
) {
106 // we must take ownership of symbolsToAdopt, even in a failure case.
107 LocalPointer
<const DecimalFormatSymbols
> adoptedSymbols(symbolsToAdopt
);
108 if (U_FAILURE(status
)) {
111 fields
= new DecimalFormatFields();
112 if (fields
== nullptr) {
113 status
= U_MEMORY_ALLOCATION_ERROR
;
116 if (adoptedSymbols
.isNull()) {
117 fields
->symbols
.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status
), status
);
119 fields
->symbols
.adoptInsteadAndCheckErrorCode(adoptedSymbols
.orphan(), status
);
121 if (U_FAILURE(status
)) {
127 #if UCONFIG_HAVE_PARSEALLINPUT
129 void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value
) {
130 if (fields
== nullptr) { return; }
131 if (value
== fields
->properties
.parseAllInput
) { return; }
132 fields
->properties
.parseAllInput
= value
;
138 DecimalFormat::setAttribute(UNumberFormatAttribute attr
, int32_t newValue
, UErrorCode
& status
) {
139 if (U_FAILURE(status
)) { return *this; }
141 if (fields
== nullptr) {
142 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
143 status
= U_MEMORY_ALLOCATION_ERROR
;
148 case UNUM_LENIENT_PARSE
:
149 setLenient(newValue
!= 0);
152 case UNUM_PARSE_INT_ONLY
:
153 setParseIntegerOnly(newValue
!= 0);
156 case UNUM_GROUPING_USED
:
157 setGroupingUsed(newValue
!= 0);
160 case UNUM_DECIMAL_ALWAYS_SHOWN
:
161 setDecimalSeparatorAlwaysShown(newValue
!= 0);
164 case UNUM_MAX_INTEGER_DIGITS
:
165 setMaximumIntegerDigits(newValue
);
168 case UNUM_MIN_INTEGER_DIGITS
:
169 setMinimumIntegerDigits(newValue
);
172 case UNUM_INTEGER_DIGITS
:
173 setMinimumIntegerDigits(newValue
);
174 setMaximumIntegerDigits(newValue
);
177 case UNUM_MAX_FRACTION_DIGITS
:
178 setMaximumFractionDigits(newValue
);
181 case UNUM_MIN_FRACTION_DIGITS
:
182 setMinimumFractionDigits(newValue
);
185 case UNUM_FRACTION_DIGITS
:
186 setMinimumFractionDigits(newValue
);
187 setMaximumFractionDigits(newValue
);
190 case UNUM_SIGNIFICANT_DIGITS_USED
:
191 setSignificantDigitsUsed(newValue
!= 0);
194 case UNUM_MAX_SIGNIFICANT_DIGITS
:
195 setMaximumSignificantDigits(newValue
);
198 case UNUM_MIN_SIGNIFICANT_DIGITS
:
199 setMinimumSignificantDigits(newValue
);
202 case UNUM_MULTIPLIER
:
203 setMultiplier(newValue
);
207 setMultiplierScale(newValue
);
210 case UNUM_GROUPING_SIZE
:
211 setGroupingSize(newValue
);
214 case UNUM_ROUNDING_MODE
:
215 setRoundingMode((DecimalFormat::ERoundingMode
) newValue
);
218 case UNUM_FORMAT_WIDTH
:
219 setFormatWidth(newValue
);
222 case UNUM_PADDING_POSITION
:
223 /** The position at which padding will take place. */
224 setPadPosition((DecimalFormat::EPadPosition
) newValue
);
227 case UNUM_SECONDARY_GROUPING_SIZE
:
228 setSecondaryGroupingSize(newValue
);
231 #if UCONFIG_HAVE_PARSEALLINPUT
232 case UNUM_PARSE_ALL_INPUT
:
233 setParseAllInput((UNumberFormatAttributeValue
) newValue
);
237 case UNUM_PARSE_NO_EXPONENT
:
238 setParseNoExponent((UBool
) newValue
);
241 case UNUM_PARSE_DECIMAL_MARK_REQUIRED
:
242 setDecimalPatternMatchRequired((UBool
) newValue
);
245 case UNUM_CURRENCY_USAGE
:
246 setCurrencyUsage((UCurrencyUsage
) newValue
, &status
);
249 case UNUM_MINIMUM_GROUPING_DIGITS
:
250 setMinimumGroupingDigits(newValue
);
253 case UNUM_PARSE_CASE_SENSITIVE
:
254 setParseCaseSensitive(static_cast<UBool
>(newValue
));
257 case UNUM_SIGN_ALWAYS_SHOWN
:
258 setSignAlwaysShown(static_cast<UBool
>(newValue
));
261 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS
:
262 setFormatFailIfMoreThanMaxDigits(static_cast<UBool
>(newValue
));
265 case UNUM_FORMAT_WITH_FULL_PRECISION
: // Apple addition for <rdar://problem/39240173>
267 bool boolVal
= UBOOL_TO_BOOL(static_cast<UBool
>(newValue
));
268 if (boolVal
!= fields
->properties
.formatFullPrecision
) {
269 fields
->properties
.formatFullPrecision
= boolVal
;
276 status
= U_UNSUPPORTED_ERROR
;
282 int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr
, UErrorCode
& status
) const {
283 if (U_FAILURE(status
)) { return -1; }
285 if (fields
== nullptr) {
286 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
287 status
= U_MEMORY_ALLOCATION_ERROR
;
292 case UNUM_LENIENT_PARSE
:
295 case UNUM_PARSE_INT_ONLY
:
296 return isParseIntegerOnly();
298 case UNUM_GROUPING_USED
:
299 return isGroupingUsed();
301 case UNUM_DECIMAL_ALWAYS_SHOWN
:
302 return isDecimalSeparatorAlwaysShown();
304 case UNUM_MAX_INTEGER_DIGITS
:
305 return getMaximumIntegerDigits();
307 case UNUM_MIN_INTEGER_DIGITS
:
308 return getMinimumIntegerDigits();
310 case UNUM_INTEGER_DIGITS
:
311 // TBD: what should this return?
312 return getMinimumIntegerDigits();
314 case UNUM_MAX_FRACTION_DIGITS
:
315 return getMaximumFractionDigits();
317 case UNUM_MIN_FRACTION_DIGITS
:
318 return getMinimumFractionDigits();
320 case UNUM_FRACTION_DIGITS
:
321 // TBD: what should this return?
322 return getMinimumFractionDigits();
324 case UNUM_SIGNIFICANT_DIGITS_USED
:
325 return areSignificantDigitsUsed();
327 case UNUM_MAX_SIGNIFICANT_DIGITS
:
328 return getMaximumSignificantDigits();
330 case UNUM_MIN_SIGNIFICANT_DIGITS
:
331 return getMinimumSignificantDigits();
333 case UNUM_MULTIPLIER
:
334 return getMultiplier();
337 return getMultiplierScale();
339 case UNUM_GROUPING_SIZE
:
340 return getGroupingSize();
342 case UNUM_ROUNDING_MODE
:
343 return getRoundingMode();
345 case UNUM_FORMAT_WIDTH
:
346 return getFormatWidth();
348 case UNUM_PADDING_POSITION
:
349 return getPadPosition();
351 case UNUM_SECONDARY_GROUPING_SIZE
:
352 return getSecondaryGroupingSize();
354 case UNUM_PARSE_NO_EXPONENT
:
355 return isParseNoExponent();
357 case UNUM_PARSE_DECIMAL_MARK_REQUIRED
:
358 return isDecimalPatternMatchRequired();
360 case UNUM_CURRENCY_USAGE
:
361 return getCurrencyUsage();
363 case UNUM_MINIMUM_GROUPING_DIGITS
:
364 return getMinimumGroupingDigits();
366 case UNUM_PARSE_CASE_SENSITIVE
:
367 return isParseCaseSensitive();
369 case UNUM_SIGN_ALWAYS_SHOWN
:
370 return isSignAlwaysShown();
372 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS
:
373 return isFormatFailIfMoreThanMaxDigits();
375 case UNUM_FORMAT_WITH_FULL_PRECISION
: // Apple addition for <rdar://problem/39240173>
376 return (UBool
)fields
->properties
.formatFullPrecision
;
379 status
= U_UNSUPPORTED_ERROR
;
383 return -1; /* undefined */
386 void DecimalFormat::setGroupingUsed(UBool enabled
) {
387 if (fields
== nullptr) {
390 if (UBOOL_TO_BOOL(enabled
) == fields
->properties
.groupingUsed
) { return; }
391 NumberFormat::setGroupingUsed(enabled
); // to set field for compatibility
392 fields
->properties
.groupingUsed
= enabled
;
396 void DecimalFormat::setParseIntegerOnly(UBool value
) {
397 if (fields
== nullptr) {
400 if (UBOOL_TO_BOOL(value
) == fields
->properties
.parseIntegerOnly
) { return; }
401 NumberFormat::setParseIntegerOnly(value
); // to set field for compatibility
402 fields
->properties
.parseIntegerOnly
= value
;
406 void DecimalFormat::setLenient(UBool enable
) {
407 if (fields
== nullptr) {
410 ParseMode mode
= enable
? PARSE_MODE_LENIENT
: PARSE_MODE_STRICT
;
411 if (!fields
->properties
.parseMode
.isNull() && mode
== fields
->properties
.parseMode
.getNoError()) { return; }
412 NumberFormat::setLenient(enable
); // to set field for compatibility
413 fields
->properties
.parseMode
= mode
;
417 DecimalFormat::DecimalFormat(const UnicodeString
& pattern
, DecimalFormatSymbols
* symbolsToAdopt
,
418 UParseError
&, UErrorCode
& status
)
419 : DecimalFormat(symbolsToAdopt
, status
) {
420 if (U_FAILURE(status
)) { return; }
421 // TODO: What is parseError for?
422 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
426 DecimalFormat::DecimalFormat(const UnicodeString
& pattern
, const DecimalFormatSymbols
& symbols
,
428 : DecimalFormat(nullptr, status
) {
429 if (U_FAILURE(status
)) { return; }
430 LocalPointer
<DecimalFormatSymbols
> dfs(new DecimalFormatSymbols(symbols
), status
);
431 if (U_FAILURE(status
)) {
432 // If we failed to allocate DecimalFormatSymbols, then release fields and its members.
433 // We must have a fully complete fields object, we cannot have partially populated members.
436 status
= U_MEMORY_ALLOCATION_ERROR
;
439 fields
->symbols
.adoptInstead(dfs
.orphan());
440 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_IF_CURRENCY
, status
);
444 DecimalFormat::DecimalFormat(const DecimalFormat
& source
) : NumberFormat(source
) {
445 // If the object that we are copying from is invalid, no point in going further.
446 if (source
.fields
== nullptr) {
449 // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have
450 // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from
451 // the property bag, despite being somewhat slower.
452 fields
= new DecimalFormatFields(source
.fields
->properties
);
453 if (fields
== nullptr) {
454 return; // no way to report an error.
456 UErrorCode status
= U_ZERO_ERROR
;
457 fields
->symbols
.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source
.fields
->symbols
), status
);
458 // In order to simplify error handling logic in the various getters/setters/etc, we do not allow
459 // any partially populated DecimalFormatFields object. We must have a fully complete fields object
460 // or else we set it to nullptr.
461 if (U_FAILURE(status
)) {
469 DecimalFormat
& DecimalFormat::operator=(const DecimalFormat
& rhs
) {
470 // guard against self-assignment
474 // Make sure both objects are valid.
475 if (fields
== nullptr || rhs
.fields
== nullptr) {
476 return *this; // unfortunately, no way to report an error.
478 fields
->properties
= rhs
.fields
->properties
;
479 fields
->exportedProperties
.clear();
480 UErrorCode status
= U_ZERO_ERROR
;
481 LocalPointer
<DecimalFormatSymbols
> dfs(new DecimalFormatSymbols(*rhs
.fields
->symbols
), status
);
482 if (U_FAILURE(status
)) {
483 // We failed to allocate DecimalFormatSymbols, release fields and its members.
484 // We must have a fully complete fields object, we cannot have partially populated members.
489 fields
->symbols
.adoptInstead(dfs
.orphan());
495 DecimalFormat::~DecimalFormat() {
496 if (fields
== nullptr) { return; }
498 delete fields
->atomicParser
.exchange(nullptr);
499 delete fields
->atomicCurrencyParser
.exchange(nullptr);
503 DecimalFormat
* DecimalFormat::clone() const {
504 // can only clone valid objects.
505 if (fields
== nullptr) {
508 LocalPointer
<DecimalFormat
> df(new DecimalFormat(*this));
509 if (df
.isValid() && df
->fields
!= nullptr) {
515 UBool
DecimalFormat::operator==(const Format
& other
) const {
516 auto* otherDF
= dynamic_cast<const DecimalFormat
*>(&other
);
517 if (otherDF
== nullptr) {
520 // If either object is in an invalid state, prevent dereferencing nullptr below.
521 // Additionally, invalid objects should not be considered equal to anything.
522 if (fields
== nullptr || otherDF
->fields
== nullptr) {
525 return fields
->properties
== otherDF
->fields
->properties
&& *fields
->symbols
== *otherDF
->fields
->symbols
;
528 UnicodeString
& DecimalFormat::format(double number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const {
529 if (fields
== nullptr) {
530 appendTo
.setToBogus();
533 if (pos
.getField() == FieldPosition::DONT_CARE
&& fastFormatDouble(number
, appendTo
)) {
536 UErrorCode localStatus
= U_ZERO_ERROR
;
537 FormattedNumber output
= fields
->formatter
.formatDouble(number
, localStatus
);
538 fieldPositionHelper(output
, pos
, appendTo
.length(), localStatus
);
539 auto appendable
= UnicodeStringAppendable(appendTo
);
540 output
.appendTo(appendable
, localStatus
);
544 UnicodeString
& DecimalFormat::format(double number
, UnicodeString
& appendTo
, FieldPosition
& pos
,
545 UErrorCode
& status
) const {
546 if (U_FAILURE(status
)) {
547 return appendTo
; // don't overwrite status if it's already a failure.
549 if (fields
== nullptr) {
550 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
551 status
= U_MEMORY_ALLOCATION_ERROR
;
552 appendTo
.setToBogus();
555 if (pos
.getField() == FieldPosition::DONT_CARE
&& fastFormatDouble(number
, appendTo
)) {
558 FormattedNumber output
= fields
->formatter
.formatDouble(number
, status
);
559 fieldPositionHelper(output
, pos
, appendTo
.length(), status
);
560 auto appendable
= UnicodeStringAppendable(appendTo
);
561 output
.appendTo(appendable
, status
);
566 DecimalFormat::format(double number
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
567 UErrorCode
& status
) const {
568 if (U_FAILURE(status
)) {
569 return appendTo
; // don't overwrite status if it's already a failure.
571 if (fields
== nullptr) {
572 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
573 status
= U_MEMORY_ALLOCATION_ERROR
;
574 appendTo
.setToBogus();
577 if (posIter
== nullptr && fastFormatDouble(number
, appendTo
)) {
580 FormattedNumber output
= fields
->formatter
.formatDouble(number
, status
);
581 fieldPositionIteratorHelper(output
, posIter
, appendTo
.length(), status
);
582 auto appendable
= UnicodeStringAppendable(appendTo
);
583 output
.appendTo(appendable
, status
);
587 UnicodeString
& DecimalFormat::format(int32_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const {
588 return format(static_cast<int64_t> (number
), appendTo
, pos
);
591 UnicodeString
& DecimalFormat::format(int32_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
,
592 UErrorCode
& status
) const {
593 return format(static_cast<int64_t> (number
), appendTo
, pos
, status
);
597 DecimalFormat::format(int32_t number
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
598 UErrorCode
& status
) const {
599 return format(static_cast<int64_t> (number
), appendTo
, posIter
, status
);
602 UnicodeString
& DecimalFormat::format(int64_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
) const {
603 if (fields
== nullptr) {
604 appendTo
.setToBogus();
607 if (pos
.getField() == FieldPosition::DONT_CARE
&& fastFormatInt64(number
, appendTo
)) {
610 UErrorCode localStatus
= U_ZERO_ERROR
;
611 FormattedNumber output
= fields
->formatter
.formatInt(number
, localStatus
);
612 fieldPositionHelper(output
, pos
, appendTo
.length(), localStatus
);
613 auto appendable
= UnicodeStringAppendable(appendTo
);
614 output
.appendTo(appendable
, localStatus
);
618 UnicodeString
& DecimalFormat::format(int64_t number
, UnicodeString
& appendTo
, FieldPosition
& pos
,
619 UErrorCode
& status
) const {
620 if (U_FAILURE(status
)) {
621 return appendTo
; // don't overwrite status if it's already a failure.
623 if (fields
== nullptr) {
624 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
625 status
= U_MEMORY_ALLOCATION_ERROR
;
626 appendTo
.setToBogus();
629 if (pos
.getField() == FieldPosition::DONT_CARE
&& fastFormatInt64(number
, appendTo
)) {
632 FormattedNumber output
= fields
->formatter
.formatInt(number
, status
);
633 fieldPositionHelper(output
, pos
, appendTo
.length(), status
);
634 auto appendable
= UnicodeStringAppendable(appendTo
);
635 output
.appendTo(appendable
, status
);
640 DecimalFormat::format(int64_t number
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
641 UErrorCode
& status
) const {
642 if (U_FAILURE(status
)) {
643 return appendTo
; // don't overwrite status if it's already a failure.
645 if (fields
== nullptr) {
646 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
647 status
= U_MEMORY_ALLOCATION_ERROR
;
648 appendTo
.setToBogus();
651 if (posIter
== nullptr && fastFormatInt64(number
, appendTo
)) {
654 FormattedNumber output
= fields
->formatter
.formatInt(number
, status
);
655 fieldPositionIteratorHelper(output
, posIter
, appendTo
.length(), status
);
656 auto appendable
= UnicodeStringAppendable(appendTo
);
657 output
.appendTo(appendable
, status
);
662 DecimalFormat::format(StringPiece number
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
663 UErrorCode
& status
) const {
664 if (U_FAILURE(status
)) {
665 return appendTo
; // don't overwrite status if it's already a failure.
667 if (fields
== nullptr) {
668 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
669 status
= U_MEMORY_ALLOCATION_ERROR
;
670 appendTo
.setToBogus();
673 FormattedNumber output
= fields
->formatter
.formatDecimal(number
, status
);
674 fieldPositionIteratorHelper(output
, posIter
, appendTo
.length(), status
);
675 auto appendable
= UnicodeStringAppendable(appendTo
);
676 output
.appendTo(appendable
, status
);
680 UnicodeString
& DecimalFormat::format(const DecimalQuantity
& number
, UnicodeString
& appendTo
,
681 FieldPositionIterator
* posIter
, UErrorCode
& status
) const {
682 if (U_FAILURE(status
)) {
683 return appendTo
; // don't overwrite status if it's already a failure.
685 if (fields
== nullptr) {
686 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
687 status
= U_MEMORY_ALLOCATION_ERROR
;
688 appendTo
.setToBogus();
691 FormattedNumber output
= fields
->formatter
.formatDecimalQuantity(number
, status
);
692 fieldPositionIteratorHelper(output
, posIter
, appendTo
.length(), status
);
693 auto appendable
= UnicodeStringAppendable(appendTo
);
694 output
.appendTo(appendable
, status
);
699 DecimalFormat::format(const DecimalQuantity
& number
, UnicodeString
& appendTo
, FieldPosition
& pos
,
700 UErrorCode
& status
) const {
701 if (U_FAILURE(status
)) {
702 return appendTo
; // don't overwrite status if it's already a failure.
704 if (fields
== nullptr) {
705 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
706 status
= U_MEMORY_ALLOCATION_ERROR
;
707 appendTo
.setToBogus();
710 FormattedNumber output
= fields
->formatter
.formatDecimalQuantity(number
, status
);
711 fieldPositionHelper(output
, pos
, appendTo
.length(), status
);
712 auto appendable
= UnicodeStringAppendable(appendTo
);
713 output
.appendTo(appendable
, status
);
717 void DecimalFormat::parse(const UnicodeString
& text
, Formattable
& output
,
718 ParsePosition
& parsePosition
) const {
719 if (fields
== nullptr) {
722 if (parsePosition
.getIndex() < 0 || parsePosition
.getIndex() >= text
.length()) {
723 if (parsePosition
.getIndex() == text
.length()) {
724 // If there is nothing to parse, it is an error
725 parsePosition
.setErrorIndex(parsePosition
.getIndex());
732 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
733 // parseCurrency method (backwards compatibility)
734 int32_t startIndex
= parsePosition
.getIndex();
735 const NumberParserImpl
* parser
= getParser(status
);
736 if (U_FAILURE(status
)) {
737 return; // unfortunately no way to report back the error.
739 parser
->parse(text
, startIndex
, true, result
, status
);
740 if (U_FAILURE(status
)) {
741 return; // unfortunately no way to report back the error.
743 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
744 if (result
.success()) {
745 parsePosition
.setIndex(result
.charEnd
);
746 result
.populateFormattable(output
, parser
->getParseFlags());
748 parsePosition
.setErrorIndex(startIndex
+ result
.charEnd
);
752 CurrencyAmount
* DecimalFormat::parseCurrency(const UnicodeString
& text
, ParsePosition
& parsePosition
) const {
753 if (fields
== nullptr) {
756 if (parsePosition
.getIndex() < 0 || parsePosition
.getIndex() >= text
.length()) {
762 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
763 // parseCurrency method (backwards compatibility)
764 int32_t startIndex
= parsePosition
.getIndex();
765 const NumberParserImpl
* parser
= getCurrencyParser(status
);
766 if (U_FAILURE(status
)) {
769 parser
->parse(text
, startIndex
, true, result
, status
);
770 if (U_FAILURE(status
)) {
773 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
774 if (result
.success()) {
775 parsePosition
.setIndex(result
.charEnd
);
776 Formattable formattable
;
777 result
.populateFormattable(formattable
, parser
->getParseFlags());
778 LocalPointer
<CurrencyAmount
> currencyAmount(
779 new CurrencyAmount(formattable
, result
.currencyCode
, status
), status
);
780 if (U_FAILURE(status
)) {
783 return currencyAmount
.orphan();
785 parsePosition
.setErrorIndex(startIndex
+ result
.charEnd
);
790 const DecimalFormatSymbols
* DecimalFormat::getDecimalFormatSymbols(void) const {
791 if (fields
== nullptr) {
794 return fields
->symbols
.getAlias();
797 void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols
* symbolsToAdopt
) {
798 if (symbolsToAdopt
== nullptr) {
799 return; // do not allow caller to set fields->symbols to NULL
801 // we must take ownership of symbolsToAdopt, even in a failure case.
802 LocalPointer
<DecimalFormatSymbols
> dfs(symbolsToAdopt
);
803 if (fields
== nullptr) {
806 fields
->symbols
.adoptInstead(dfs
.orphan());
810 void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols
& symbols
) {
811 if (fields
== nullptr) {
814 UErrorCode status
= U_ZERO_ERROR
;
815 LocalPointer
<DecimalFormatSymbols
> dfs(new DecimalFormatSymbols(symbols
), status
);
816 if (U_FAILURE(status
)) {
817 // We failed to allocate DecimalFormatSymbols, release fields and its members.
818 // We must have a fully complete fields object, we cannot have partially populated members.
823 fields
->symbols
.adoptInstead(dfs
.orphan());
827 const CurrencyPluralInfo
* DecimalFormat::getCurrencyPluralInfo(void) const {
828 if (fields
== nullptr) {
831 return fields
->properties
.currencyPluralInfo
.fPtr
.getAlias();
834 void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo
* toAdopt
) {
835 // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols?
836 // we must take ownership of toAdopt, even in a failure case.
837 LocalPointer
<CurrencyPluralInfo
> cpi(toAdopt
);
838 if (fields
== nullptr) {
841 fields
->properties
.currencyPluralInfo
.fPtr
.adoptInstead(cpi
.orphan());
845 void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo
& info
) {
846 if (fields
== nullptr) {
849 if (fields
->properties
.currencyPluralInfo
.fPtr
.isNull()) {
850 // Note: clone() can fail with OOM error, but we have no way to report it. :(
851 fields
->properties
.currencyPluralInfo
.fPtr
.adoptInstead(info
.clone());
853 *fields
->properties
.currencyPluralInfo
.fPtr
= info
; // copy-assignment operator
858 UnicodeString
& DecimalFormat::getPositivePrefix(UnicodeString
& result
) const {
859 if (fields
== nullptr) {
863 UErrorCode status
= U_ZERO_ERROR
;
864 fields
->formatter
.getAffixImpl(true, false, result
, status
);
865 if (U_FAILURE(status
)) { result
.setToBogus(); }
869 void DecimalFormat::setPositivePrefix(const UnicodeString
& newValue
) {
870 if (fields
== nullptr) {
873 if (newValue
== fields
->properties
.positivePrefix
) { return; }
874 fields
->properties
.positivePrefix
= newValue
;
878 UnicodeString
& DecimalFormat::getNegativePrefix(UnicodeString
& result
) const {
879 if (fields
== nullptr) {
883 UErrorCode status
= U_ZERO_ERROR
;
884 fields
->formatter
.getAffixImpl(true, true, result
, status
);
885 if (U_FAILURE(status
)) { result
.setToBogus(); }
889 void DecimalFormat::setNegativePrefix(const UnicodeString
& newValue
) {
890 if (fields
== nullptr) {
893 if (newValue
== fields
->properties
.negativePrefix
) { return; }
894 fields
->properties
.negativePrefix
= newValue
;
898 UnicodeString
& DecimalFormat::getPositiveSuffix(UnicodeString
& result
) const {
899 if (fields
== nullptr) {
903 UErrorCode status
= U_ZERO_ERROR
;
904 fields
->formatter
.getAffixImpl(false, false, result
, status
);
905 if (U_FAILURE(status
)) { result
.setToBogus(); }
909 void DecimalFormat::setPositiveSuffix(const UnicodeString
& newValue
) {
910 if (fields
== nullptr) {
913 if (newValue
== fields
->properties
.positiveSuffix
) { return; }
914 fields
->properties
.positiveSuffix
= newValue
;
918 UnicodeString
& DecimalFormat::getNegativeSuffix(UnicodeString
& result
) const {
919 if (fields
== nullptr) {
923 UErrorCode status
= U_ZERO_ERROR
;
924 fields
->formatter
.getAffixImpl(false, true, result
, status
);
925 if (U_FAILURE(status
)) { result
.setToBogus(); }
929 void DecimalFormat::setNegativeSuffix(const UnicodeString
& newValue
) {
930 if (fields
== nullptr) {
933 if (newValue
== fields
->properties
.negativeSuffix
) { return; }
934 fields
->properties
.negativeSuffix
= newValue
;
938 UBool
DecimalFormat::isSignAlwaysShown() const {
939 // Not much we can do to report an error.
940 if (fields
== nullptr) {
941 return DecimalFormatProperties::getDefault().signAlwaysShown
;
943 return fields
->properties
.signAlwaysShown
;
946 void DecimalFormat::setSignAlwaysShown(UBool value
) {
947 if (fields
== nullptr) { return; }
948 if (UBOOL_TO_BOOL(value
) == fields
->properties
.signAlwaysShown
) { return; }
949 fields
->properties
.signAlwaysShown
= value
;
953 int32_t DecimalFormat::getMultiplier(void) const {
954 const DecimalFormatProperties
*dfp
;
955 // Not much we can do to report an error.
956 if (fields
== nullptr) {
957 // Fallback to using the default instance of DecimalFormatProperties.
958 dfp
= &(DecimalFormatProperties::getDefault());
960 dfp
= &fields
->properties
;
962 if (dfp
->multiplier
!= 1) {
963 return dfp
->multiplier
;
964 } else if (dfp
->magnitudeMultiplier
!= 0) {
965 return static_cast<int32_t>(uprv_pow10(dfp
->magnitudeMultiplier
));
971 void DecimalFormat::setMultiplier(int32_t multiplier
) {
972 if (fields
== nullptr) {
975 if (multiplier
== 0) {
976 multiplier
= 1; // one being the benign default value for a multiplier.
979 // Try to convert to a magnitude multiplier first
981 int value
= multiplier
;
984 int temp
= value
/ 10;
985 if (temp
* 10 != value
) {
992 fields
->properties
.magnitudeMultiplier
= delta
;
993 fields
->properties
.multiplier
= 1;
995 fields
->properties
.magnitudeMultiplier
= 0;
996 fields
->properties
.multiplier
= multiplier
;
1001 int32_t DecimalFormat::getMultiplierScale() const {
1002 // Not much we can do to report an error.
1003 if (fields
== nullptr) {
1004 // Fallback to using the default instance of DecimalFormatProperties.
1005 return DecimalFormatProperties::getDefault().multiplierScale
;
1007 return fields
->properties
.multiplierScale
;
1010 void DecimalFormat::setMultiplierScale(int32_t newValue
) {
1011 if (fields
== nullptr) { return; }
1012 if (newValue
== fields
->properties
.multiplierScale
) { return; }
1013 fields
->properties
.multiplierScale
= newValue
;
1017 double DecimalFormat::getRoundingIncrement(void) const {
1018 // Not much we can do to report an error.
1019 if (fields
== nullptr) {
1020 // Fallback to using the default instance of DecimalFormatProperties.
1021 return DecimalFormatProperties::getDefault().roundingIncrement
;
1023 return fields
->exportedProperties
.roundingIncrement
;
1026 void DecimalFormat::setRoundingIncrement(double newValue
) {
1027 if (fields
== nullptr) { return; }
1028 if (newValue
== fields
->properties
.roundingIncrement
) { return; }
1029 fields
->properties
.roundingIncrement
= newValue
;
1033 ERoundingMode
DecimalFormat::getRoundingMode(void) const {
1034 // Not much we can do to report an error.
1035 if (fields
== nullptr) {
1036 // Fallback to using the default instance of DecimalFormatProperties.
1037 return static_cast<ERoundingMode
>(DecimalFormatProperties::getDefault().roundingMode
.getNoError());
1039 // UNumberFormatRoundingMode and ERoundingMode have the same values.
1040 return static_cast<ERoundingMode
>(fields
->exportedProperties
.roundingMode
.getNoError());
1043 void DecimalFormat::setRoundingMode(ERoundingMode roundingMode
) {
1044 if (fields
== nullptr) { return; }
1045 auto uRoundingMode
= static_cast<UNumberFormatRoundingMode
>(roundingMode
);
1046 if (!fields
->properties
.roundingMode
.isNull() && uRoundingMode
== fields
->properties
.roundingMode
.getNoError()) {
1049 NumberFormat::setMaximumIntegerDigits(roundingMode
); // to set field for compatibility
1050 fields
->properties
.roundingMode
= uRoundingMode
;
1054 int32_t DecimalFormat::getFormatWidth(void) const {
1055 // Not much we can do to report an error.
1056 if (fields
== nullptr) {
1057 // Fallback to using the default instance of DecimalFormatProperties.
1058 return DecimalFormatProperties::getDefault().formatWidth
;
1060 return fields
->properties
.formatWidth
;
1063 void DecimalFormat::setFormatWidth(int32_t width
) {
1064 if (fields
== nullptr) { return; }
1065 if (width
== fields
->properties
.formatWidth
) { return; }
1066 fields
->properties
.formatWidth
= width
;
1070 UnicodeString
DecimalFormat::getPadCharacterString() const {
1071 if (fields
== nullptr || fields
->properties
.padString
.isBogus()) {
1072 // Readonly-alias the static string kFallbackPaddingString
1073 return {TRUE
, kFallbackPaddingString
, -1};
1075 return fields
->properties
.padString
;
1079 void DecimalFormat::setPadCharacter(const UnicodeString
& padChar
) {
1080 if (fields
== nullptr) { return; }
1081 if (padChar
== fields
->properties
.padString
) { return; }
1082 if (padChar
.length() > 0) {
1083 fields
->properties
.padString
= UnicodeString(padChar
.char32At(0));
1085 fields
->properties
.padString
.setToBogus();
1090 EPadPosition
DecimalFormat::getPadPosition(void) const {
1091 if (fields
== nullptr || fields
->properties
.padPosition
.isNull()) {
1092 return EPadPosition::kPadBeforePrefix
;
1094 // UNumberFormatPadPosition and EPadPosition have the same values.
1095 return static_cast<EPadPosition
>(fields
->properties
.padPosition
.getNoError());
1099 void DecimalFormat::setPadPosition(EPadPosition padPos
) {
1100 if (fields
== nullptr) { return; }
1101 auto uPadPos
= static_cast<UNumberFormatPadPosition
>(padPos
);
1102 if (!fields
->properties
.padPosition
.isNull() && uPadPos
== fields
->properties
.padPosition
.getNoError()) {
1105 fields
->properties
.padPosition
= uPadPos
;
1109 UBool
DecimalFormat::isScientificNotation(void) const {
1110 // Not much we can do to report an error.
1111 if (fields
== nullptr) {
1112 // Fallback to using the default instance of DecimalFormatProperties.
1113 return (DecimalFormatProperties::getDefault().minimumExponentDigits
!= -1);
1115 return (fields
->properties
.minimumExponentDigits
!= -1);
1118 void DecimalFormat::setScientificNotation(UBool useScientific
) {
1119 if (fields
== nullptr) { return; }
1120 int32_t minExp
= useScientific
? 1 : -1;
1121 if (fields
->properties
.minimumExponentDigits
== minExp
) { return; }
1122 if (useScientific
) {
1123 fields
->properties
.minimumExponentDigits
= 1;
1125 fields
->properties
.minimumExponentDigits
= -1;
1130 int8_t DecimalFormat::getMinimumExponentDigits(void) const {
1131 // Not much we can do to report an error.
1132 if (fields
== nullptr) {
1133 // Fallback to using the default instance of DecimalFormatProperties.
1134 return static_cast<int8_t>(DecimalFormatProperties::getDefault().minimumExponentDigits
);
1136 return static_cast<int8_t>(fields
->properties
.minimumExponentDigits
);
1139 void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig
) {
1140 if (fields
== nullptr) { return; }
1141 if (minExpDig
== fields
->properties
.minimumExponentDigits
) { return; }
1142 fields
->properties
.minimumExponentDigits
= minExpDig
;
1146 UBool
DecimalFormat::isExponentSignAlwaysShown(void) const {
1147 // Not much we can do to report an error.
1148 if (fields
== nullptr) {
1149 // Fallback to using the default instance of DecimalFormatProperties.
1150 return DecimalFormatProperties::getDefault().exponentSignAlwaysShown
;
1152 return fields
->properties
.exponentSignAlwaysShown
;
1155 void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways
) {
1156 if (fields
== nullptr) { return; }
1157 if (UBOOL_TO_BOOL(expSignAlways
) == fields
->properties
.exponentSignAlwaysShown
) { return; }
1158 fields
->properties
.exponentSignAlwaysShown
= expSignAlways
;
1162 int32_t DecimalFormat::getGroupingSize(void) const {
1163 int32_t groupingSize
;
1164 // Not much we can do to report an error.
1165 if (fields
== nullptr) {
1166 // Fallback to using the default instance of DecimalFormatProperties.
1167 groupingSize
= DecimalFormatProperties::getDefault().groupingSize
;
1169 groupingSize
= fields
->properties
.groupingSize
;
1171 if (groupingSize
< 0) {
1174 return groupingSize
;
1177 void DecimalFormat::setGroupingSize(int32_t newValue
) {
1178 if (fields
== nullptr) { return; }
1179 if (newValue
== fields
->properties
.groupingSize
) { return; }
1180 fields
->properties
.groupingSize
= newValue
;
1184 int32_t DecimalFormat::getSecondaryGroupingSize(void) const {
1186 // Not much we can do to report an error.
1187 if (fields
== nullptr) {
1188 // Fallback to using the default instance of DecimalFormatProperties.
1189 grouping2
= DecimalFormatProperties::getDefault().secondaryGroupingSize
;
1191 grouping2
= fields
->properties
.secondaryGroupingSize
;
1193 if (grouping2
< 0) {
1199 void DecimalFormat::setSecondaryGroupingSize(int32_t newValue
) {
1200 if (fields
== nullptr) { return; }
1201 if (newValue
== fields
->properties
.secondaryGroupingSize
) { return; }
1202 fields
->properties
.secondaryGroupingSize
= newValue
;
1206 int32_t DecimalFormat::getMinimumGroupingDigits() const {
1207 // Not much we can do to report an error.
1208 if (fields
== nullptr) {
1209 // Fallback to using the default instance of DecimalFormatProperties.
1210 return DecimalFormatProperties::getDefault().minimumGroupingDigits
;
1212 return fields
->properties
.minimumGroupingDigits
;
1215 void DecimalFormat::setMinimumGroupingDigits(int32_t newValue
) {
1216 if (fields
== nullptr) { return; }
1217 if (newValue
== fields
->properties
.minimumGroupingDigits
) { return; }
1218 fields
->properties
.minimumGroupingDigits
= newValue
;
1222 UBool
DecimalFormat::isDecimalSeparatorAlwaysShown(void) const {
1223 // Not much we can do to report an error.
1224 if (fields
== nullptr) {
1225 // Fallback to using the default instance of DecimalFormatProperties.
1226 return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown
;
1228 return fields
->properties
.decimalSeparatorAlwaysShown
;
1231 void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue
) {
1232 if (fields
== nullptr) { return; }
1233 if (UBOOL_TO_BOOL(newValue
) == fields
->properties
.decimalSeparatorAlwaysShown
) { return; }
1234 fields
->properties
.decimalSeparatorAlwaysShown
= newValue
;
1238 UBool
DecimalFormat::isDecimalPatternMatchRequired(void) const {
1239 // Not much we can do to report an error.
1240 if (fields
== nullptr) {
1241 // Fallback to using the default instance of DecimalFormatProperties.
1242 return DecimalFormatProperties::getDefault().decimalPatternMatchRequired
;
1244 return fields
->properties
.decimalPatternMatchRequired
;
1247 void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue
) {
1248 if (fields
== nullptr) { return; }
1249 if (UBOOL_TO_BOOL(newValue
) == fields
->properties
.decimalPatternMatchRequired
) { return; }
1250 fields
->properties
.decimalPatternMatchRequired
= newValue
;
1254 UBool
DecimalFormat::isParseNoExponent() const {
1255 // Not much we can do to report an error.
1256 if (fields
== nullptr) {
1257 // Fallback to using the default instance of DecimalFormatProperties.
1258 return DecimalFormatProperties::getDefault().parseNoExponent
;
1260 return fields
->properties
.parseNoExponent
;
1263 void DecimalFormat::setParseNoExponent(UBool value
) {
1264 if (fields
== nullptr) { return; }
1265 if (UBOOL_TO_BOOL(value
) == fields
->properties
.parseNoExponent
) { return; }
1266 fields
->properties
.parseNoExponent
= value
;
1270 UBool
DecimalFormat::isParseCaseSensitive() const {
1271 // Not much we can do to report an error.
1272 if (fields
== nullptr) {
1273 // Fallback to using the default instance of DecimalFormatProperties.
1274 return DecimalFormatProperties::getDefault().parseCaseSensitive
;
1276 return fields
->properties
.parseCaseSensitive
;
1279 void DecimalFormat::setParseCaseSensitive(UBool value
) {
1280 if (fields
== nullptr) { return; }
1281 if (UBOOL_TO_BOOL(value
) == fields
->properties
.parseCaseSensitive
) { return; }
1282 fields
->properties
.parseCaseSensitive
= value
;
1286 UBool
DecimalFormat::isFormatFailIfMoreThanMaxDigits() const {
1287 // Not much we can do to report an error.
1288 if (fields
== nullptr) {
1289 // Fallback to using the default instance of DecimalFormatProperties.
1290 return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits
;
1292 return fields
->properties
.formatFailIfMoreThanMaxDigits
;
1295 void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value
) {
1296 if (fields
== nullptr) { return; }
1297 if (UBOOL_TO_BOOL(value
) == fields
->properties
.formatFailIfMoreThanMaxDigits
) { return; }
1298 fields
->properties
.formatFailIfMoreThanMaxDigits
= value
;
1302 UnicodeString
& DecimalFormat::toPattern(UnicodeString
& result
) const {
1303 if (fields
== nullptr) {
1304 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1305 result
.setToBogus();
1308 // Pull some properties from exportedProperties and others from properties
1309 // to keep affix patterns intact. In particular, pull rounding properties
1310 // so that CurrencyUsage is reflected properly.
1311 // TODO: Consider putting this logic in number_patternstring.cpp instead.
1312 ErrorCode localStatus
;
1313 DecimalFormatProperties
tprops(fields
->properties
);
1314 bool useCurrency
= (
1315 !tprops
.currency
.isNull() ||
1316 !tprops
.currencyPluralInfo
.fPtr
.isNull() ||
1317 !tprops
.currencyUsage
.isNull() ||
1318 AffixUtils::hasCurrencySymbols(tprops
.positivePrefixPattern
, localStatus
) ||
1319 AffixUtils::hasCurrencySymbols(tprops
.positiveSuffixPattern
, localStatus
) ||
1320 AffixUtils::hasCurrencySymbols(tprops
.negativePrefixPattern
, localStatus
) ||
1321 AffixUtils::hasCurrencySymbols(tprops
.negativeSuffixPattern
, localStatus
));
1323 tprops
.minimumFractionDigits
= fields
->exportedProperties
.minimumFractionDigits
;
1324 tprops
.maximumFractionDigits
= fields
->exportedProperties
.maximumFractionDigits
;
1325 tprops
.roundingIncrement
= fields
->exportedProperties
.roundingIncrement
;
1327 result
= PatternStringUtils::propertiesToPatternString(tprops
, localStatus
);
1331 UnicodeString
& DecimalFormat::toLocalizedPattern(UnicodeString
& result
) const {
1332 if (fields
== nullptr) {
1333 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1334 result
.setToBogus();
1337 ErrorCode localStatus
;
1338 result
= toPattern(result
);
1339 result
= PatternStringUtils::convertLocalized(result
, *fields
->symbols
, true, localStatus
);
1343 void DecimalFormat::applyPattern(const UnicodeString
& pattern
, UParseError
&, UErrorCode
& status
) {
1344 // TODO: What is parseError for?
1345 applyPattern(pattern
, status
);
1348 void DecimalFormat::applyPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
1349 // don't overwrite status if it's already a failure.
1350 if (U_FAILURE(status
)) { return; }
1351 if (fields
== nullptr) {
1352 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1353 status
= U_MEMORY_ALLOCATION_ERROR
;
1356 setPropertiesFromPattern(pattern
, IGNORE_ROUNDING_NEVER
, status
);
1360 void DecimalFormat::applyLocalizedPattern(const UnicodeString
& localizedPattern
, UParseError
&,
1361 UErrorCode
& status
) {
1362 // TODO: What is parseError for?
1363 applyLocalizedPattern(localizedPattern
, status
);
1366 void DecimalFormat::applyLocalizedPattern(const UnicodeString
& localizedPattern
, UErrorCode
& status
) {
1367 // don't overwrite status if it's already a failure.
1368 if (U_FAILURE(status
)) { return; }
1369 if (fields
== nullptr) {
1370 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1371 status
= U_MEMORY_ALLOCATION_ERROR
;
1374 UnicodeString pattern
= PatternStringUtils::convertLocalized(
1375 localizedPattern
, *fields
->symbols
, false, status
);
1376 applyPattern(pattern
, status
);
1379 void DecimalFormat::setMaximumIntegerDigits(int32_t newValue
) {
1380 if (fields
== nullptr) { return; }
1381 if (newValue
== fields
->properties
.maximumIntegerDigits
) { return; }
1382 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1383 int32_t min
= fields
->properties
.minimumIntegerDigits
;
1384 if (min
>= 0 && min
> newValue
) {
1385 fields
->properties
.minimumIntegerDigits
= newValue
;
1387 fields
->properties
.maximumIntegerDigits
= newValue
;
1391 void DecimalFormat::setMinimumIntegerDigits(int32_t newValue
) {
1392 if (fields
== nullptr) { return; }
1393 if (newValue
== fields
->properties
.minimumIntegerDigits
) { return; }
1394 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1395 int32_t max
= fields
->properties
.maximumIntegerDigits
;
1396 if (max
>= 0 && max
< newValue
) {
1397 fields
->properties
.maximumIntegerDigits
= newValue
;
1399 fields
->properties
.minimumIntegerDigits
= newValue
;
1403 void DecimalFormat::setMaximumFractionDigits(int32_t newValue
) {
1404 if (fields
== nullptr) { return; }
1405 if (newValue
== fields
->properties
.maximumFractionDigits
) { return; }
1406 // cap for backward compatibility, limit to 340 <rdar://problem/50113359>
1407 if (newValue
> 340) {
1410 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1411 int32_t min
= fields
->properties
.minimumFractionDigits
;
1412 if (min
>= 0 && min
> newValue
) {
1413 fields
->properties
.minimumFractionDigits
= newValue
;
1415 fields
->properties
.maximumFractionDigits
= newValue
;
1419 void DecimalFormat::setMinimumFractionDigits(int32_t newValue
) {
1420 if (fields
== nullptr) { return; }
1421 if (newValue
== fields
->properties
.minimumFractionDigits
) { return; }
1422 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1423 int32_t max
= fields
->properties
.maximumFractionDigits
;
1424 if (max
>= 0 && max
< newValue
) {
1425 fields
->properties
.maximumFractionDigits
= newValue
;
1427 fields
->properties
.minimumFractionDigits
= newValue
;
1431 int32_t DecimalFormat::getMinimumSignificantDigits() const {
1432 // Not much we can do to report an error.
1433 if (fields
== nullptr) {
1434 // Fallback to using the default instance of DecimalFormatProperties.
1435 return DecimalFormatProperties::getDefault().minimumSignificantDigits
;
1437 return fields
->exportedProperties
.minimumSignificantDigits
;
1440 int32_t DecimalFormat::getMaximumSignificantDigits() const {
1441 // Not much we can do to report an error.
1442 if (fields
== nullptr) {
1443 // Fallback to using the default instance of DecimalFormatProperties.
1444 return DecimalFormatProperties::getDefault().maximumSignificantDigits
;
1446 return fields
->exportedProperties
.maximumSignificantDigits
;
1449 void DecimalFormat::setMinimumSignificantDigits(int32_t value
) {
1450 if (fields
== nullptr) { return; }
1451 if (value
== fields
->properties
.minimumSignificantDigits
) { return; }
1452 int32_t max
= fields
->properties
.maximumSignificantDigits
;
1453 if (max
>= 0 && max
< value
) {
1454 fields
->properties
.maximumSignificantDigits
= value
;
1456 fields
->properties
.minimumSignificantDigits
= value
;
1460 void DecimalFormat::setMaximumSignificantDigits(int32_t value
) {
1461 if (fields
== nullptr) { return; }
1462 if (value
== fields
->properties
.maximumSignificantDigits
) { return; }
1463 int32_t min
= fields
->properties
.minimumSignificantDigits
;
1464 if (min
>= 0 && min
> value
) {
1465 fields
->properties
.minimumSignificantDigits
= value
;
1467 fields
->properties
.maximumSignificantDigits
= value
;
1471 UBool
DecimalFormat::areSignificantDigitsUsed() const {
1472 const DecimalFormatProperties
* dfp
;
1473 // Not much we can do to report an error.
1474 if (fields
== nullptr) {
1475 // Fallback to using the default instance of DecimalFormatProperties.
1476 dfp
= &(DecimalFormatProperties::getDefault());
1478 dfp
= &fields
->properties
;
1480 return dfp
->minimumSignificantDigits
!= -1 || dfp
->maximumSignificantDigits
!= -1;
1483 void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits
) {
1484 if (fields
== nullptr) { return; }
1486 // These are the default values from the old implementation.
1487 if (useSignificantDigits
) {
1488 if (fields
->properties
.minimumSignificantDigits
!= -1 ||
1489 fields
->properties
.maximumSignificantDigits
!= -1) {
1493 if (fields
->properties
.minimumSignificantDigits
== -1 &&
1494 fields
->properties
.maximumSignificantDigits
== -1) {
1498 int32_t minSig
= useSignificantDigits
? 1 : -1;
1499 int32_t maxSig
= useSignificantDigits
? 6 : -1;
1500 fields
->properties
.minimumSignificantDigits
= minSig
;
1501 fields
->properties
.maximumSignificantDigits
= maxSig
;
1505 // Group-set several settings used for numbers in date formats. Apple rdar://50064762
1507 // setGroupingUsed(FALSE);
1508 // setDecimalSeparatorAlwaysShown(FALSE);
1509 // setParseIntegerOnly(TRUE);
1510 // setMinimumFractionDigits(0);
1511 void DecimalFormat::setDateSettings(void) {
1512 if (fields
== nullptr) {
1515 UBool didChange
= FALSE
;
1517 if (fields
->properties
.groupingUsed
) {
1518 NumberFormat::setGroupingUsed(FALSE
); // to set field for compatibility
1519 fields
->properties
.groupingUsed
= false;
1523 if (fields
->properties
.decimalSeparatorAlwaysShown
) {
1524 fields
->properties
.decimalSeparatorAlwaysShown
= false;
1528 if (!fields
->properties
.parseIntegerOnly
) {
1529 NumberFormat::setParseIntegerOnly(TRUE
); // to set field for compatibility
1530 fields
->properties
.parseIntegerOnly
= true;
1534 if (fields
->properties
.minimumFractionDigits
!= 0) {
1535 fields
->properties
.minimumFractionDigits
= 0;
1544 void DecimalFormat::setCurrency(const char16_t* theCurrency
, UErrorCode
& ec
) {
1545 // don't overwrite ec if it's already a failure.
1546 if (U_FAILURE(ec
)) { return; }
1547 if (fields
== nullptr) {
1548 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1549 ec
= U_MEMORY_ALLOCATION_ERROR
;
1552 // <rdar://problem/49544607> Restore behavior in which empty currency sets locale default
1553 UChar localeCurr
[4];
1554 if (theCurrency
==nullptr || theCurrency
[0]==0) {
1555 UErrorCode getCurrStatus
= U_ZERO_ERROR
;
1556 int32_t currLen
= ucurr_forLocale(fields
->symbols
->getLocale().getName(), localeCurr
, UPRV_LENGTHOF(localeCurr
), &getCurrStatus
);
1557 if (U_SUCCESS(getCurrStatus
) && currLen
==3) {
1559 theCurrency
= localeCurr
;
1563 CurrencyUnit
currencyUnit(theCurrency
, ec
);
1564 if (U_FAILURE(ec
)) { return; }
1565 if (!fields
->properties
.currency
.isNull() && fields
->properties
.currency
.getNoError() == currencyUnit
) {
1568 NumberFormat::setCurrency(theCurrency
, ec
); // to set field for compatibility
1569 fields
->properties
.currency
= currencyUnit
;
1570 // TODO: Set values in fields->symbols, too?
1574 void DecimalFormat::setCurrency(const char16_t* theCurrency
) {
1575 ErrorCode localStatus
;
1576 setCurrency(theCurrency
, localStatus
);
1579 void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage
, UErrorCode
* ec
) {
1580 // don't overwrite ec if it's already a failure.
1581 if (U_FAILURE(*ec
)) { return; }
1582 if (fields
== nullptr) {
1583 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1584 *ec
= U_MEMORY_ALLOCATION_ERROR
;
1587 if (!fields
->properties
.currencyUsage
.isNull() && newUsage
== fields
->properties
.currencyUsage
.getNoError()) {
1590 fields
->properties
.currencyUsage
= newUsage
;
1594 UCurrencyUsage
DecimalFormat::getCurrencyUsage() const {
1595 // CurrencyUsage is not exported, so we have to get it from the input property bag.
1596 // TODO: Should we export CurrencyUsage instead?
1597 if (fields
== nullptr || fields
->properties
.currencyUsage
.isNull()) {
1598 return UCURR_USAGE_STANDARD
;
1600 return fields
->properties
.currencyUsage
.getNoError();
1604 DecimalFormat::formatToDecimalQuantity(double number
, DecimalQuantity
& output
, UErrorCode
& status
) const {
1605 // don't overwrite status if it's already a failure.
1606 if (U_FAILURE(status
)) { return; }
1607 if (fields
== nullptr) {
1608 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1609 status
= U_MEMORY_ALLOCATION_ERROR
;
1612 fields
->formatter
.formatDouble(number
, status
).getDecimalQuantity(output
, status
);
1615 void DecimalFormat::formatToDecimalQuantity(const Formattable
& number
, DecimalQuantity
& output
,
1616 UErrorCode
& status
) const {
1617 // don't overwrite status if it's already a failure.
1618 if (U_FAILURE(status
)) { return; }
1619 if (fields
== nullptr) {
1620 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1621 status
= U_MEMORY_ALLOCATION_ERROR
;
1624 UFormattedNumberData obj
;
1625 number
.populateDecimalQuantity(obj
.quantity
, status
);
1626 fields
->formatter
.formatImpl(&obj
, status
);
1627 output
= std::move(obj
.quantity
);
1630 const number::LocalizedNumberFormatter
* DecimalFormat::toNumberFormatter(UErrorCode
& status
) const {
1631 // We sometimes need to return nullptr here (see ICU-20380)
1632 if (U_FAILURE(status
)) { return nullptr; }
1633 if (fields
== nullptr) {
1634 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1635 status
= U_MEMORY_ALLOCATION_ERROR
;
1638 return &fields
->formatter
;
1641 // Apple <rdar://problem/49955427>
1642 void DecimalFormat::setDFSShallowCopy(UBool shallow
) {
1643 if (fields
!= nullptr) {
1644 fields
->formatter
.setDFSShallowCopy(shallow
);
1648 /** Rebuilds the formatter object from the property bag. */
1649 void DecimalFormat::touch(UErrorCode
& status
) {
1650 if (U_FAILURE(status
)) {
1653 if (fields
== nullptr) {
1654 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1655 // For regular construction, the caller should have checked the status variable for errors.
1656 // For copy construction, there is unfortunately nothing to report the error, so we need to guard against
1657 // this possible bad state here and set the status to an error.
1658 status
= U_MEMORY_ALLOCATION_ERROR
;
1662 // In C++, fields->symbols is the source of truth for the locale.
1663 Locale locale
= fields
->symbols
->getLocale();
1665 // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties,
1666 // so automatically recompute it here. The parser is a bit more expensive and is not needed until the
1667 // parse method is called, so defer that until needed.
1668 // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
1670 // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object
1671 // and don't need to call new. (Which is slower and could possibly fail).
1672 fields
->formatter
= NumberPropertyMapper::create(
1673 fields
->properties
, *fields
->symbols
, fields
->warehouse
, fields
->exportedProperties
, status
1676 // Do this after fields->exportedProperties are set up
1679 // Delete the parsers if they were made previously
1680 delete fields
->atomicParser
.exchange(nullptr);
1681 delete fields
->atomicCurrencyParser
.exchange(nullptr);
1683 // In order for the getters to work, we need to populate some fields in NumberFormat.
1684 const UChar
* newCurr
= u
"";
1685 CurrencyUnit currency
= fields
->exportedProperties
.currency
.get(status
);
1686 if (U_SUCCESS(status
)) {
1687 // currency.getISOCurrency() is an inline that just returns a pointer to currency's
1688 // internal field char16_t isoCode[4], cannot be NULL if currency is valid:
1689 newCurr
= (const UChar
*)currency
.getISOCurrency();
1690 // NumberFormat::getCurrency() just returns a pointer to the superclass's
1691 // internal field char16_t fCurrency[4], cannot be NULL:
1692 const UChar
* haveCurr
= (const UChar
*)NumberFormat::getCurrency();
1693 if (u_strcmp(newCurr
,u
"XXX")==0 && u_strcmp(haveCurr
,u
"XXX")!=0) { // <rdar://51985640>
1694 // We did not get here via DecimalFormat::setCurrency(u"XXX", ...)
1698 NumberFormat::setCurrency(newCurr
, status
);
1699 NumberFormat::setMaximumIntegerDigits(fields
->exportedProperties
.maximumIntegerDigits
);
1700 NumberFormat::setMinimumIntegerDigits(fields
->exportedProperties
.minimumIntegerDigits
);
1701 NumberFormat::setMaximumFractionDigits(fields
->exportedProperties
.maximumFractionDigits
);
1702 NumberFormat::setMinimumFractionDigits(fields
->exportedProperties
.minimumFractionDigits
);
1703 // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
1704 NumberFormat::setGroupingUsed(fields
->properties
.groupingUsed
);
1707 void DecimalFormat::touchNoError() {
1708 UErrorCode localStatus
= U_ZERO_ERROR
;
1712 void DecimalFormat::setPropertiesFromPattern(const UnicodeString
& pattern
, int32_t ignoreRounding
,
1713 UErrorCode
& status
) {
1714 if (U_SUCCESS(status
)) {
1715 // Cast workaround to get around putting the enum in the public header file
1716 auto actualIgnoreRounding
= static_cast<IgnoreRounding
>(ignoreRounding
);
1717 PatternParser::parseToExistingProperties(pattern
, fields
->properties
, actualIgnoreRounding
, status
);
1721 const numparse::impl::NumberParserImpl
* DecimalFormat::getParser(UErrorCode
& status
) const {
1722 // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp)
1725 if (U_FAILURE(status
)) {
1729 // First try to get the pre-computed parser
1730 auto* ptr
= fields
->atomicParser
.load();
1731 if (ptr
!= nullptr) {
1735 // Try computing the parser on our own
1736 auto* temp
= NumberParserImpl::createParserFromProperties(fields
->properties
, *fields
->symbols
, false, status
);
1737 if (U_FAILURE(status
)) {
1740 if (temp
== nullptr) {
1741 status
= U_MEMORY_ALLOCATION_ERROR
;
1745 // Note: ptr starts as nullptr; during compare_exchange,
1746 // it is set to what is actually stored in the atomic
1747 // if another thread beat us to computing the parser object.
1748 auto* nonConstThis
= const_cast<DecimalFormat
*>(this);
1749 if (!nonConstThis
->fields
->atomicParser
.compare_exchange_strong(ptr
, temp
)) {
1750 // Another thread beat us to computing the parser
1754 // Our copy of the parser got stored in the atomic
1759 const numparse::impl::NumberParserImpl
* DecimalFormat::getCurrencyParser(UErrorCode
& status
) const {
1760 if (U_FAILURE(status
)) { return nullptr; }
1762 // First try to get the pre-computed parser
1763 auto* ptr
= fields
->atomicCurrencyParser
.load();
1764 if (ptr
!= nullptr) {
1768 // Try computing the parser on our own
1769 auto* temp
= NumberParserImpl::createParserFromProperties(fields
->properties
, *fields
->symbols
, true, status
);
1770 if (temp
== nullptr) {
1771 status
= U_MEMORY_ALLOCATION_ERROR
;
1772 // although we may still dereference, call sites should be guarded
1775 // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
1776 // atomic if another thread beat us to computing the parser object.
1777 auto* nonConstThis
= const_cast<DecimalFormat
*>(this);
1778 if (!nonConstThis
->fields
->atomicCurrencyParser
.compare_exchange_strong(ptr
, temp
)) {
1779 // Another thread beat us to computing the parser
1783 // Our copy of the parser got stored in the atomic
1789 DecimalFormat::fieldPositionHelper(const number::FormattedNumber
& formatted
, FieldPosition
& fieldPosition
,
1790 int32_t offset
, UErrorCode
& status
) {
1791 if (U_FAILURE(status
)) { return; }
1792 // always return first occurrence:
1793 fieldPosition
.setBeginIndex(0);
1794 fieldPosition
.setEndIndex(0);
1795 bool found
= formatted
.nextFieldPosition(fieldPosition
, status
);
1796 if (found
&& offset
!= 0) {
1797 FieldPositionOnlyHandler
fpoh(fieldPosition
);
1798 fpoh
.shiftLast(offset
);
1803 DecimalFormat::fieldPositionIteratorHelper(const number::FormattedNumber
& formatted
, FieldPositionIterator
* fpi
,
1804 int32_t offset
, UErrorCode
& status
) {
1805 if (U_SUCCESS(status
) && (fpi
!= nullptr)) {
1806 FieldPositionIteratorHandler
fpih(fpi
, status
);
1807 fpih
.setShift(offset
);
1808 formatted
.getAllFieldPositionsImpl(fpih
, status
);
1812 // To debug fast-format, change void(x) to printf(x)
1813 #define trace(x) void(x)
1815 void DecimalFormat::setupFastFormat() {
1816 // Check the majority of properties:
1817 if (!fields
->properties
.equalsDefaultExceptFastFormat()) {
1818 trace("no fast format: equality\n");
1819 fields
->canUseFastFormat
= false;
1823 // Now check the remaining properties.
1824 // Nontrivial affixes:
1825 UBool trivialPP
= fields
->properties
.positivePrefixPattern
.isEmpty();
1826 UBool trivialPS
= fields
->properties
.positiveSuffixPattern
.isEmpty();
1827 UBool trivialNP
= fields
->properties
.negativePrefixPattern
.isBogus() || (
1828 fields
->properties
.negativePrefixPattern
.length() == 1 &&
1829 fields
->properties
.negativePrefixPattern
.charAt(0) == u
'-');
1830 UBool trivialNS
= fields
->properties
.negativeSuffixPattern
.isEmpty();
1831 if (!trivialPP
|| !trivialPS
|| !trivialNP
|| !trivialNS
) {
1832 trace("no fast format: affixes\n");
1833 fields
->canUseFastFormat
= false;
1837 // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat):
1838 bool groupingUsed
= fields
->properties
.groupingUsed
;
1839 int32_t groupingSize
= fields
->properties
.groupingSize
;
1840 bool unusualGroupingSize
= groupingSize
> 0 && groupingSize
!= 3;
1841 const UnicodeString
& groupingString
= fields
->symbols
->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol
);
1842 if (groupingUsed
&& (unusualGroupingSize
|| groupingString
.length() != 1)) {
1843 trace("no fast format: grouping\n");
1844 fields
->canUseFastFormat
= false;
1849 int32_t minInt
= fields
->exportedProperties
.minimumIntegerDigits
;
1850 int32_t maxInt
= fields
->exportedProperties
.maximumIntegerDigits
;
1851 // Fastpath supports up to only 10 digits (length of INT32_MIN)
1853 trace("no fast format: integer\n");
1854 fields
->canUseFastFormat
= false;
1858 // Fraction length (no fraction part allowed in fast path):
1859 int32_t minFrac
= fields
->exportedProperties
.minimumFractionDigits
;
1861 trace("no fast format: fraction\n");
1862 fields
->canUseFastFormat
= false;
1867 const UnicodeString
& minusSignString
= fields
->symbols
->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol
);
1868 UChar32 codePointZero
= fields
->symbols
->getCodePointZero();
1869 if (minusSignString
.length() != 1 || U16_LENGTH(codePointZero
) != 1) {
1870 trace("no fast format: symbols\n");
1871 fields
->canUseFastFormat
= false;
1876 trace("can use fast format!\n");
1877 fields
->canUseFastFormat
= true;
1878 fields
->fastData
.cpZero
= static_cast<char16_t>(codePointZero
);
1879 fields
->fastData
.cpGroupingSeparator
= groupingUsed
&& groupingSize
== 3 ? groupingString
.charAt(0) : 0;
1880 fields
->fastData
.cpMinusSign
= minusSignString
.charAt(0);
1881 fields
->fastData
.minInt
= (minInt
< 0 || minInt
> 127) ? 0 : static_cast<int8_t>(minInt
);
1882 fields
->fastData
.maxInt
= (maxInt
< 0 || maxInt
> 127) ? 127 : static_cast<int8_t>(maxInt
);
1885 bool DecimalFormat::fastFormatDouble(double input
, UnicodeString
& output
) const {
1886 if (!fields
->canUseFastFormat
) {
1889 if (std::isnan(input
)
1890 || std::trunc(input
) != input
1891 || input
<= INT32_MIN
1892 || input
> INT32_MAX
) {
1895 doFastFormatInt32(static_cast<int32_t>(input
), std::signbit(input
), output
);
1899 bool DecimalFormat::fastFormatInt64(int64_t input
, UnicodeString
& output
) const {
1900 if (!fields
->canUseFastFormat
) {
1903 if (input
<= INT32_MIN
|| input
> INT32_MAX
) {
1906 doFastFormatInt32(static_cast<int32_t>(input
), input
< 0, output
);
1910 void DecimalFormat::doFastFormatInt32(int32_t input
, bool isNegative
, UnicodeString
& output
) const {
1911 U_ASSERT(fields
->canUseFastFormat
);
1913 output
.append(fields
->fastData
.cpMinusSign
);
1914 U_ASSERT(input
!= INT32_MIN
); // handled by callers
1917 // Cap at int32_t to make the buffer small and operations fast.
1918 // Longest string: "2,147,483,648" (13 chars in length)
1919 static constexpr int32_t localCapacity
= 13;
1920 char16_t localBuffer
[localCapacity
];
1921 char16_t* ptr
= localBuffer
+ localCapacity
;
1923 int8_t minInt
= (fields
->fastData
.minInt
< 1)? 1: fields
->fastData
.minInt
; // rdar://54569257
1924 for (int8_t i
= 0; i
< fields
->fastData
.maxInt
&& (input
!= 0 || i
< minInt
); i
++) {
1925 if (group
++ == 3 && fields
->fastData
.cpGroupingSeparator
!= 0) {
1926 *(--ptr
) = fields
->fastData
.cpGroupingSeparator
;
1929 std::div_t res
= std::div(input
, 10);
1930 *(--ptr
) = static_cast<char16_t>(fields
->fastData
.cpZero
+ res
.rem
);
1933 int32_t len
= localCapacity
- static_cast<int32_t>(ptr
- localBuffer
);
1934 output
.append(ptr
, len
);
1938 #endif /* #if !UCONFIG_NO_FORMATTING */