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
14 #include "number_decnum.h"
15 #include "number_types.h"
16 #include "number_utils.h"
18 #include "decContext.h"
19 #include "decNumber.h"
20 #include "double-conversion.h"
26 using namespace icu::number
;
27 using namespace icu::number::impl
;
29 using icu::double_conversion::DoubleToStringConverter
;
35 doGetPattern(UResourceBundle
* res
, const char* nsName
, const char* patternKey
, UErrorCode
& publicStatus
,
36 UErrorCode
& localStatus
) {
37 // Construct the path into the resource bundle
39 key
.append("NumberElements/", publicStatus
);
40 key
.append(nsName
, publicStatus
);
41 key
.append("/patterns/", publicStatus
);
42 key
.append(patternKey
, publicStatus
);
43 if (U_FAILURE(publicStatus
)) {
46 return ures_getStringByKeyWithFallback(res
, key
.data(), nullptr, &localStatus
);
52 const char16_t* utils::getPatternForStyle(const Locale
& locale
, const char* nsName
, CldrPatternStyle style
,
54 const char* patternKey
;
56 case CLDR_PATTERN_STYLE_DECIMAL
:
57 patternKey
= "decimalFormat";
59 case CLDR_PATTERN_STYLE_CURRENCY
:
60 patternKey
= "currencyFormat";
62 case CLDR_PATTERN_STYLE_ACCOUNTING
:
63 patternKey
= "accountingFormat";
65 case CLDR_PATTERN_STYLE_PERCENT
:
66 patternKey
= "percentFormat";
68 case CLDR_PATTERN_STYLE_SCIENTIFIC
:
69 patternKey
= "scientificFormat";
72 patternKey
= "decimalFormat"; // silence compiler error
75 LocalUResourceBundlePointer res
;
76 if (style
== CLDR_PATTERN_STYLE_PERCENT
) {
77 // for percent pattern, always use language fallback (see rdar://63758323)
78 res
.adoptInstead(ures_open(nullptr, locale
.getName(), &status
));
80 // for the other patterns, use country fallback when appropriate
81 res
.adoptInstead(ures_openWithCountryFallback(nullptr, locale
.getName(), NULL
, &status
));
83 if (U_FAILURE(status
)) { return u
""; }
85 // Attempt to get the pattern with the native numbering system.
86 UErrorCode localStatus
= U_ZERO_ERROR
;
87 const char16_t* pattern
;
88 pattern
= doGetPattern(res
.getAlias(), nsName
, patternKey
, status
, localStatus
);
89 if (U_FAILURE(status
)) { return u
""; }
91 // Fall back to latn if native numbering system does not have the right pattern
92 if (U_FAILURE(localStatus
) && uprv_strcmp("latn", nsName
) != 0) {
93 localStatus
= U_ZERO_ERROR
;
94 pattern
= doGetPattern(res
.getAlias(), "latn", patternKey
, status
, localStatus
);
95 if (U_FAILURE(status
)) { return u
""; }
103 uprv_decContextDefault(&fContext
, DEC_INIT_BASE
);
104 uprv_decContextSetRounding(&fContext
, DEC_ROUND_HALF_EVEN
);
105 fContext
.traps
= 0; // no traps, thank you (what does this even mean?)
108 DecNum::DecNum(const DecNum
& other
, UErrorCode
& status
)
109 : fContext(other
.fContext
) {
110 // Allocate memory for the new DecNum.
111 U_ASSERT(fContext
.digits
== other
.fData
.getCapacity());
112 if (fContext
.digits
> kDefaultDigits
) {
113 void* p
= fData
.resize(fContext
.digits
, 0);
115 status
= U_MEMORY_ALLOCATION_ERROR
;
120 // Copy the data from the old DecNum to the new one.
121 uprv_memcpy(fData
.getAlias(), other
.fData
.getAlias(), sizeof(decNumber
));
122 uprv_memcpy(fData
.getArrayStart(),
123 other
.fData
.getArrayStart(),
124 other
.fData
.getArrayLimit() - other
.fData
.getArrayStart());
127 void DecNum::setTo(StringPiece str
, UErrorCode
& status
) {
128 // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.
129 CharString
cstr(str
, status
);
130 if (U_FAILURE(status
)) { return; }
131 _setTo(cstr
.data(), str
.length(), status
);
134 void DecNum::setTo(const char* str
, UErrorCode
& status
) {
135 _setTo(str
, static_cast<int32_t>(uprv_strlen(str
)), status
);
138 void DecNum::setTo(double d
, UErrorCode
& status
) {
139 // Need to check for NaN and Infinity before going into DoubleToStringConverter
140 if (std::isnan(d
) != 0 || std::isfinite(d
) == 0) {
141 status
= U_UNSUPPORTED_ERROR
;
145 // First convert from double to string, then string to DecNum.
146 // Allocate enough room for: all digits, "E-324", and NUL-terminator.
147 char buffer
[DoubleToStringConverter::kBase10MaximalLength
+ 6];
148 bool sign
; // unused; always positive
151 DoubleToStringConverter::DoubleToAscii(
153 DoubleToStringConverter::DtoaMode::SHORTEST
,
162 // Read initial result as a string.
163 _setTo(buffer
, length
, status
);
165 // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.
166 fData
.getAlias()->exponent
+= point
- length
;
167 fData
.getAlias()->bits
|= static_cast<uint8_t>(std::signbit(d
) ? DECNEG
: 0);
170 void DecNum::_setTo(const char* str
, int32_t maxDigits
, UErrorCode
& status
) {
171 if (maxDigits
> kDefaultDigits
) {
172 fData
.resize(maxDigits
, 0);
173 fContext
.digits
= maxDigits
;
175 fContext
.digits
= kDefaultDigits
;
178 static_assert(DECDPUN
== 1, "Assumes that DECDPUN is set to 1");
179 uprv_decNumberFromString(fData
.getAlias(), str
, &fContext
);
181 // Check for invalid syntax and set the corresponding error code.
182 if ((fContext
.status
& DEC_Conversion_syntax
) != 0) {
183 status
= U_DECIMAL_NUMBER_SYNTAX_ERROR
;
185 } else if (fContext
.status
!= 0) {
186 // Not a syntax error, but some other error, like an exponent that is too large.
187 status
= U_UNSUPPORTED_ERROR
;
191 // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
192 if (decNumberIsSpecial(fData
.getAlias())) {
193 status
= U_UNSUPPORTED_ERROR
;
199 DecNum::setTo(const uint8_t* bcd
, int32_t length
, int32_t scale
, bool isNegative
, UErrorCode
& status
) {
200 if (length
> kDefaultDigits
) {
201 fData
.resize(length
, 0);
202 fContext
.digits
= length
;
204 fContext
.digits
= kDefaultDigits
;
207 // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
208 if (length
< 1 || length
> 999999999) {
209 // Too large for decNumber
210 status
= U_UNSUPPORTED_ERROR
;
213 // "The exponent field holds the exponent of the number. Its range is limited by the requirement that
214 // "the range of the adjusted exponent of the number be balanced and fit within a whole number of
215 // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted
216 // "exponent is the exponent that would result if the number were expressed with a single digit before
217 // "the decimal point, and is therefore given by exponent+digits-1."
218 if (scale
> 999999999 - length
+ 1 || scale
< -999999999 - length
+ 1) {
219 // Too large for decNumber
220 status
= U_UNSUPPORTED_ERROR
;
224 fData
.getAlias()->digits
= length
;
225 fData
.getAlias()->exponent
= scale
;
226 fData
.getAlias()->bits
= static_cast<uint8_t>(isNegative
? DECNEG
: 0);
227 uprv_decNumberSetBCD(fData
, bcd
, static_cast<uint32_t>(length
));
228 if (fContext
.status
!= 0) {
229 // Some error occurred while constructing the decNumber.
230 status
= U_INTERNAL_PROGRAM_ERROR
;
234 void DecNum::normalize() {
235 uprv_decNumberReduce(fData
, fData
, &fContext
);
238 void DecNum::multiplyBy(const DecNum
& rhs
, UErrorCode
& status
) {
239 uprv_decNumberMultiply(fData
, fData
, rhs
.fData
, &fContext
);
240 if (fContext
.status
!= 0) {
241 status
= U_INTERNAL_PROGRAM_ERROR
;
245 void DecNum::divideBy(const DecNum
& rhs
, UErrorCode
& status
) {
246 uprv_decNumberDivide(fData
, fData
, rhs
.fData
, &fContext
);
247 if ((fContext
.status
& DEC_Inexact
) != 0) {
249 } else if (fContext
.status
!= 0) {
250 status
= U_INTERNAL_PROGRAM_ERROR
;
254 bool DecNum::isNegative() const {
255 return decNumberIsNegative(fData
.getAlias());
258 bool DecNum::isZero() const {
259 return decNumberIsZero(fData
.getAlias());
262 void DecNum::toString(ByteSink
& output
, UErrorCode
& status
) const {
263 if (U_FAILURE(status
)) {
266 // "string must be at least dn->digits+14 characters long"
267 int32_t minCapacity
= fData
.getAlias()->digits
+ 14;
268 MaybeStackArray
<char, 30> buffer(minCapacity
);
269 uprv_decNumberToString(fData
, buffer
.getAlias());
270 output
.Append(buffer
.getAlias(), static_cast<int32_t>(uprv_strlen(buffer
.getAlias())));
273 #endif /* #if !UCONFIG_NO_FORMATTING */