]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/number_utils.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / number_utils.cpp
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 <stdlib.h>
13 #include <cmath>
14 #include "number_decnum.h"
15 #include "number_types.h"
16 #include "number_utils.h"
17 #include "charstr.h"
18 #include "decContext.h"
19 #include "decNumber.h"
20 #include "double-conversion.h"
21 #include "fphdlimp.h"
22 #include "uresimp.h"
23 #include "ureslocs.h"
24
25 using namespace icu;
26 using namespace icu::number;
27 using namespace icu::number::impl;
28
29 using icu::double_conversion::DoubleToStringConverter;
30
31
32 namespace {
33
34 const char16_t*
35 doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, UErrorCode& publicStatus,
36 UErrorCode& localStatus) {
37 // Construct the path into the resource bundle
38 CharString key;
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)) {
44 return u"";
45 }
46 return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);
47 }
48
49 }
50
51
52 const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style,
53 UErrorCode& status) {
54 const char* patternKey;
55 switch (style) {
56 case CLDR_PATTERN_STYLE_DECIMAL:
57 patternKey = "decimalFormat";
58 break;
59 case CLDR_PATTERN_STYLE_CURRENCY:
60 patternKey = "currencyFormat";
61 break;
62 case CLDR_PATTERN_STYLE_ACCOUNTING:
63 patternKey = "accountingFormat";
64 break;
65 case CLDR_PATTERN_STYLE_PERCENT:
66 patternKey = "percentFormat";
67 break;
68 case CLDR_PATTERN_STYLE_SCIENTIFIC:
69 patternKey = "scientificFormat";
70 break;
71 default:
72 patternKey = "decimalFormat"; // silence compiler error
73 UPRV_UNREACHABLE;
74 }
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));
79 } else {
80 // for the other patterns, use country fallback when appropriate
81 res.adoptInstead(ures_openWithCountryFallback(nullptr, locale.getName(), NULL, &status));
82 }
83 if (U_FAILURE(status)) { return u""; }
84
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""; }
90
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""; }
96 }
97
98 return pattern;
99 }
100
101
102 DecNum::DecNum() {
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?)
106 }
107
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);
114 if (p == nullptr) {
115 status = U_MEMORY_ALLOCATION_ERROR;
116 return;
117 }
118 }
119
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());
125 }
126
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);
132 }
133
134 void DecNum::setTo(const char* str, UErrorCode& status) {
135 _setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);
136 }
137
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;
142 return;
143 }
144
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
149 int32_t length;
150 int32_t point;
151 DoubleToStringConverter::DoubleToAscii(
152 d,
153 DoubleToStringConverter::DtoaMode::SHORTEST,
154 0,
155 buffer,
156 sizeof(buffer),
157 &sign,
158 &length,
159 &point
160 );
161
162 // Read initial result as a string.
163 _setTo(buffer, length, status);
164
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);
168 }
169
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;
174 } else {
175 fContext.digits = kDefaultDigits;
176 }
177
178 static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
179 uprv_decNumberFromString(fData.getAlias(), str, &fContext);
180
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;
184 return;
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;
188 return;
189 }
190
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;
194 return;
195 }
196 }
197
198 void
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;
203 } else {
204 fContext.digits = kDefaultDigits;
205 }
206
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;
211 return;
212 }
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;
221 return;
222 }
223
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;
231 }
232 }
233
234 void DecNum::normalize() {
235 uprv_decNumberReduce(fData, fData, &fContext);
236 }
237
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;
242 }
243 }
244
245 void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
246 uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
247 if ((fContext.status & DEC_Inexact) != 0) {
248 // Ignore.
249 } else if (fContext.status != 0) {
250 status = U_INTERNAL_PROGRAM_ERROR;
251 }
252 }
253
254 bool DecNum::isNegative() const {
255 return decNumberIsNegative(fData.getAlias());
256 }
257
258 bool DecNum::isZero() const {
259 return decNumberIsZero(fData.getAlias());
260 }
261
262 void DecNum::toString(ByteSink& output, UErrorCode& status) const {
263 if (U_FAILURE(status)) {
264 return;
265 }
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())));
271 }
272
273 #endif /* #if !UCONFIG_NO_FORMATTING */