/src/icu/source/i18n/number_utils.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
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  | 0  |              UErrorCode& localStatus) { | 
37  |  |     // Construct the path into the resource bundle  | 
38  | 0  |     CharString key;  | 
39  | 0  |     key.append("NumberElements/", publicStatus); | 
40  | 0  |     key.append(nsName, publicStatus);  | 
41  | 0  |     key.append("/patterns/", publicStatus); | 
42  | 0  |     key.append(patternKey, publicStatus);  | 
43  | 0  |     if (U_FAILURE(publicStatus)) { | 
44  | 0  |         return u"";  | 
45  | 0  |     }  | 
46  | 0  |     return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);  | 
47  | 0  | }  | 
48  |  |  | 
49  |  | }  | 
50  |  |  | 
51  |  |  | 
52  |  | const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style,  | 
53  | 0  |                                           UErrorCode& status) { | 
54  | 0  |     const char* patternKey;  | 
55  | 0  |     switch (style) { | 
56  | 0  |         case CLDR_PATTERN_STYLE_DECIMAL:  | 
57  | 0  |             patternKey = "decimalFormat";  | 
58  | 0  |             break;  | 
59  | 0  |         case CLDR_PATTERN_STYLE_CURRENCY:  | 
60  | 0  |             patternKey = "currencyFormat";  | 
61  | 0  |             break;  | 
62  | 0  |         case CLDR_PATTERN_STYLE_ACCOUNTING:  | 
63  | 0  |             patternKey = "accountingFormat";  | 
64  | 0  |             break;  | 
65  | 0  |         case CLDR_PATTERN_STYLE_PERCENT:  | 
66  | 0  |             patternKey = "percentFormat";  | 
67  | 0  |             break;  | 
68  | 0  |         case CLDR_PATTERN_STYLE_SCIENTIFIC:  | 
69  | 0  |             patternKey = "scientificFormat";  | 
70  | 0  |             break;  | 
71  | 0  |         default:  | 
72  | 0  |             patternKey = "decimalFormat"; // silence compiler error  | 
73  | 0  |             UPRV_UNREACHABLE;  | 
74  | 0  |     }  | 
75  | 0  |     LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));  | 
76  | 0  |     if (U_FAILURE(status)) { return u""; } | 
77  |  |  | 
78  |  |     // Attempt to get the pattern with the native numbering system.  | 
79  | 0  |     UErrorCode localStatus = U_ZERO_ERROR;  | 
80  | 0  |     const char16_t* pattern;  | 
81  | 0  |     pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);  | 
82  | 0  |     if (U_FAILURE(status)) { return u""; } | 
83  |  |  | 
84  |  |     // Fall back to latn if native numbering system does not have the right pattern  | 
85  | 0  |     if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) { | 
86  | 0  |         localStatus = U_ZERO_ERROR;  | 
87  | 0  |         pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);  | 
88  | 0  |         if (U_FAILURE(status)) { return u""; } | 
89  | 0  |     }  | 
90  |  |  | 
91  | 0  |     return pattern;  | 
92  | 0  | }  | 
93  |  |  | 
94  |  |  | 
95  | 0  | DecNum::DecNum() { | 
96  | 0  |     uprv_decContextDefault(&fContext, DEC_INIT_BASE);  | 
97  | 0  |     uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);  | 
98  | 0  |     fContext.traps = 0; // no traps, thank you (what does this even mean?)  | 
99  | 0  | }  | 
100  |  |  | 
101  |  | DecNum::DecNum(const DecNum& other, UErrorCode& status)  | 
102  | 0  |         : fContext(other.fContext) { | 
103  |  |     // Allocate memory for the new DecNum.  | 
104  | 0  |     U_ASSERT(fContext.digits == other.fData.getCapacity());  | 
105  | 0  |     if (fContext.digits > kDefaultDigits) { | 
106  | 0  |         void* p = fData.resize(fContext.digits, 0);  | 
107  | 0  |         if (p == nullptr) { | 
108  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
109  | 0  |             return;  | 
110  | 0  |         }  | 
111  | 0  |     }  | 
112  |  |  | 
113  |  |     // Copy the data from the old DecNum to the new one.  | 
114  | 0  |     uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber));  | 
115  | 0  |     uprv_memcpy(fData.getArrayStart(),  | 
116  | 0  |             other.fData.getArrayStart(),  | 
117  | 0  |             other.fData.getArrayLimit() - other.fData.getArrayStart());  | 
118  | 0  | }  | 
119  |  |  | 
120  | 0  | void DecNum::setTo(StringPiece str, UErrorCode& status) { | 
121  |  |     // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.  | 
122  | 0  |     CharString cstr(str, status);  | 
123  | 0  |     if (U_FAILURE(status)) { return; } | 
124  | 0  |     _setTo(cstr.data(), str.length(), status);  | 
125  | 0  | }  | 
126  |  |  | 
127  | 0  | void DecNum::setTo(const char* str, UErrorCode& status) { | 
128  | 0  |     _setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);  | 
129  | 0  | }  | 
130  |  |  | 
131  | 0  | void DecNum::setTo(double d, UErrorCode& status) { | 
132  |  |     // Need to check for NaN and Infinity before going into DoubleToStringConverter  | 
133  | 0  |     if (std::isnan(d) != 0 || std::isfinite(d) == 0) { | 
134  | 0  |         status = U_UNSUPPORTED_ERROR;  | 
135  | 0  |         return;  | 
136  | 0  |     }  | 
137  |  |  | 
138  |  |     // First convert from double to string, then string to DecNum.  | 
139  |  |     // Allocate enough room for: all digits, "E-324", and NUL-terminator.  | 
140  | 0  |     char buffer[DoubleToStringConverter::kBase10MaximalLength + 6];  | 
141  | 0  |     bool sign; // unused; always positive  | 
142  | 0  |     int32_t length;  | 
143  | 0  |     int32_t point;  | 
144  | 0  |     DoubleToStringConverter::DoubleToAscii(  | 
145  | 0  |             d,  | 
146  | 0  |             DoubleToStringConverter::DtoaMode::SHORTEST,  | 
147  | 0  |             0,  | 
148  | 0  |             buffer,  | 
149  | 0  |             sizeof(buffer),  | 
150  | 0  |             &sign,  | 
151  | 0  |             &length,  | 
152  | 0  |             &point  | 
153  | 0  |     );  | 
154  |  |  | 
155  |  |     // Read initial result as a string.  | 
156  | 0  |     _setTo(buffer, length, status);  | 
157  |  |  | 
158  |  |     // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.  | 
159  | 0  |     fData.getAlias()->exponent += point - length;  | 
160  | 0  |     fData.getAlias()->bits |= static_cast<uint8_t>(std::signbit(d) ? DECNEG : 0);  | 
161  | 0  | }  | 
162  |  |  | 
163  | 0  | void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) { | 
164  | 0  |     if (maxDigits > kDefaultDigits) { | 
165  | 0  |         fData.resize(maxDigits, 0);  | 
166  | 0  |         fContext.digits = maxDigits;  | 
167  | 0  |     } else { | 
168  | 0  |         fContext.digits = kDefaultDigits;  | 
169  | 0  |     }  | 
170  |  | 
  | 
171  | 0  |     static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");  | 
172  | 0  |     uprv_decNumberFromString(fData.getAlias(), str, &fContext);  | 
173  |  |  | 
174  |  |     // Check for invalid syntax and set the corresponding error code.  | 
175  | 0  |     if ((fContext.status & DEC_Conversion_syntax) != 0) { | 
176  | 0  |         status = U_DECIMAL_NUMBER_SYNTAX_ERROR;  | 
177  | 0  |         return;  | 
178  | 0  |     } else if (fContext.status != 0) { | 
179  |  |         // Not a syntax error, but some other error, like an exponent that is too large.  | 
180  | 0  |         status = U_UNSUPPORTED_ERROR;  | 
181  | 0  |         return;  | 
182  | 0  |     }  | 
183  |  |  | 
184  |  |     // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!  | 
185  | 0  |     if (decNumberIsSpecial(fData.getAlias())) { | 
186  | 0  |         status = U_UNSUPPORTED_ERROR;  | 
187  | 0  |         return;  | 
188  | 0  |     }  | 
189  | 0  | }  | 
190  |  |  | 
191  |  | void  | 
192  | 0  | DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) { | 
193  | 0  |     if (length > kDefaultDigits) { | 
194  | 0  |         fData.resize(length, 0);  | 
195  | 0  |         fContext.digits = length;  | 
196  | 0  |     } else { | 
197  | 0  |         fContext.digits = kDefaultDigits;  | 
198  | 0  |     }  | 
199  |  |  | 
200  |  |     // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."  | 
201  | 0  |     if (length < 1 || length > 999999999) { | 
202  |  |         // Too large for decNumber  | 
203  | 0  |         status = U_UNSUPPORTED_ERROR;  | 
204  | 0  |         return;  | 
205  | 0  |     }  | 
206  |  |     // "The exponent field holds the exponent of the number. Its range is limited by the requirement that  | 
207  |  |     // "the range of the adjusted exponent of the number be balanced and fit within a whole number of  | 
208  |  |     // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted  | 
209  |  |     // "exponent is the exponent that would result if the number were expressed with a single digit before  | 
210  |  |     // "the decimal point, and is therefore given by exponent+digits-1."  | 
211  | 0  |     if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) { | 
212  |  |         // Too large for decNumber  | 
213  | 0  |         status = U_UNSUPPORTED_ERROR;  | 
214  | 0  |         return;  | 
215  | 0  |     }  | 
216  |  |  | 
217  | 0  |     fData.getAlias()->digits = length;  | 
218  | 0  |     fData.getAlias()->exponent = scale;  | 
219  | 0  |     fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);  | 
220  | 0  |     uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));  | 
221  | 0  |     if (fContext.status != 0) { | 
222  |  |         // Some error occurred while constructing the decNumber.  | 
223  | 0  |         status = U_INTERNAL_PROGRAM_ERROR;  | 
224  | 0  |     }  | 
225  | 0  | }  | 
226  |  |  | 
227  | 0  | void DecNum::normalize() { | 
228  | 0  |     uprv_decNumberReduce(fData, fData, &fContext);  | 
229  | 0  | }  | 
230  |  |  | 
231  | 0  | void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) { | 
232  | 0  |     uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);  | 
233  | 0  |     if (fContext.status != 0) { | 
234  | 0  |         status = U_INTERNAL_PROGRAM_ERROR;  | 
235  | 0  |     }  | 
236  | 0  | }  | 
237  |  |  | 
238  | 0  | void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) { | 
239  | 0  |     uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);  | 
240  | 0  |     if ((fContext.status & DEC_Inexact) != 0) { | 
241  |  |         // Ignore.  | 
242  | 0  |     } else if (fContext.status != 0) { | 
243  | 0  |         status = U_INTERNAL_PROGRAM_ERROR;  | 
244  | 0  |     }  | 
245  | 0  | }  | 
246  |  |  | 
247  | 0  | bool DecNum::isNegative() const { | 
248  | 0  |     return decNumberIsNegative(fData.getAlias());  | 
249  | 0  | }  | 
250  |  |  | 
251  | 0  | bool DecNum::isZero() const { | 
252  | 0  |     return decNumberIsZero(fData.getAlias());  | 
253  | 0  | }  | 
254  |  |  | 
255  | 0  | void DecNum::toString(ByteSink& output, UErrorCode& status) const { | 
256  | 0  |     if (U_FAILURE(status)) { | 
257  | 0  |         return;  | 
258  | 0  |     }  | 
259  |  |     // "string must be at least dn->digits+14 characters long"  | 
260  | 0  |     int32_t minCapacity = fData.getAlias()->digits + 14;  | 
261  | 0  |     MaybeStackArray<char, 30> buffer(minCapacity, status);  | 
262  | 0  |     if (U_FAILURE(status)) { | 
263  | 0  |         return;  | 
264  | 0  |     }  | 
265  | 0  |     uprv_decNumberToString(fData, buffer.getAlias());  | 
266  | 0  |     output.Append(buffer.getAlias(), static_cast<int32_t>(uprv_strlen(buffer.getAlias())));  | 
267  | 0  | }  | 
268  |  |  | 
269  |  | #endif /* #if !UCONFIG_NO_FORMATTING */  |