• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 // Allow implicit conversion from char16_t* to UnicodeString for this file:
9 // Helpful in toString methods and elsewhere.
10 #define UNISTR_FROM_STRING_EXPLICIT
11 
12 #include <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*
doGetPattern(UResourceBundle * res,const char * nsName,const char * patternKey,UErrorCode & publicStatus,UErrorCode & localStatus)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 
getPatternForStyle(const Locale & locale,const char * nsName,CldrPatternStyle style,UErrorCode & status)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_EXIT;
74     }
75     LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));
76     if (U_FAILURE(status)) { return u""; }
77 
78     // Attempt to get the pattern with the native numbering system.
79     UErrorCode localStatus = U_ZERO_ERROR;
80     const char16_t* pattern;
81     pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);
82     if (U_FAILURE(status)) { return u""; }
83 
84     // Fall back to latn if native numbering system does not have the right pattern
85     if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) {
86         localStatus = U_ZERO_ERROR;
87         pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);
88         if (U_FAILURE(status)) { return u""; }
89     }
90 
91     return pattern;
92 }
93 
94 
DecNum()95 DecNum::DecNum() {
96     uprv_decContextDefault(&fContext, DEC_INIT_BASE);
97     uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
98     fContext.traps = 0; // no traps, thank you (what does this even mean?)
99 }
100 
DecNum(const DecNum & other,UErrorCode & status)101 DecNum::DecNum(const DecNum& other, UErrorCode& status)
102         : fContext(other.fContext) {
103     // Allocate memory for the new DecNum.
104     U_ASSERT(fContext.digits == other.fData.getCapacity());
105     if (fContext.digits > kDefaultDigits) {
106         void* p = fData.resize(fContext.digits, 0);
107         if (p == nullptr) {
108             status = U_MEMORY_ALLOCATION_ERROR;
109             return;
110         }
111     }
112 
113     // Copy the data from the old DecNum to the new one.
114     uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber));
115     uprv_memcpy(fData.getArrayStart(),
116             other.fData.getArrayStart(),
117             other.fData.getArrayLimit() - other.fData.getArrayStart());
118 }
119 
setTo(StringPiece str,UErrorCode & status)120 void DecNum::setTo(StringPiece str, UErrorCode& status) {
121     // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.
122     CharString cstr(str, status);
123     if (U_FAILURE(status)) { return; }
124     _setTo(cstr.data(), str.length(), status);
125 }
126 
setTo(const char * str,UErrorCode & status)127 void DecNum::setTo(const char* str, UErrorCode& status) {
128     _setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);
129 }
130 
setTo(double d,UErrorCode & status)131 void DecNum::setTo(double d, UErrorCode& status) {
132     // Need to check for NaN and Infinity before going into DoubleToStringConverter
133     if (std::isnan(d) != 0 || std::isfinite(d) == 0) {
134         status = U_UNSUPPORTED_ERROR;
135         return;
136     }
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     char buffer[DoubleToStringConverter::kBase10MaximalLength + 6];
141     bool sign; // unused; always positive
142     int32_t length;
143     int32_t point;
144     DoubleToStringConverter::DoubleToAscii(
145             d,
146             DoubleToStringConverter::DtoaMode::SHORTEST,
147             0,
148             buffer,
149             sizeof(buffer),
150             &sign,
151             &length,
152             &point
153     );
154 
155     // Read initial result as a string.
156     _setTo(buffer, length, status);
157 
158     // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.
159     fData.getAlias()->exponent += point - length;
160     fData.getAlias()->bits |= static_cast<uint8_t>(std::signbit(d) ? DECNEG : 0);
161 }
162 
_setTo(const char * str,int32_t maxDigits,UErrorCode & status)163 void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) {
164     if (maxDigits > kDefaultDigits) {
165         fData.resize(maxDigits, 0);
166         fContext.digits = maxDigits;
167     } else {
168         fContext.digits = kDefaultDigits;
169     }
170 
171     static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
172     uprv_decNumberFromString(fData.getAlias(), str, &fContext);
173 
174     // Check for invalid syntax and set the corresponding error code.
175     if ((fContext.status & DEC_Conversion_syntax) != 0) {
176         status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
177         return;
178     } else if (fContext.status != 0) {
179         // Not a syntax error, but some other error, like an exponent that is too large.
180         status = U_UNSUPPORTED_ERROR;
181         return;
182     }
183 }
184 
185 void
setTo(const uint8_t * bcd,int32_t length,int32_t scale,bool isNegative,UErrorCode & status)186 DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) {
187     if (length > kDefaultDigits) {
188         fData.resize(length, 0);
189         fContext.digits = length;
190     } else {
191         fContext.digits = kDefaultDigits;
192     }
193 
194     // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
195     if (length < 1 || length > 999999999) {
196         // Too large for decNumber
197         status = U_UNSUPPORTED_ERROR;
198         return;
199     }
200     // "The exponent field holds the exponent of the number. Its range is limited by the requirement that
201     // "the range of the adjusted exponent of the number be balanced and fit within a whole number of
202     // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted
203     // "exponent is the exponent that would result if the number were expressed with a single digit before
204     // "the decimal point, and is therefore given by exponent+digits-1."
205     if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) {
206         // Too large for decNumber
207         status = U_UNSUPPORTED_ERROR;
208         return;
209     }
210 
211     fData.getAlias()->digits = length;
212     fData.getAlias()->exponent = scale;
213     fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);
214     uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));
215     if (fContext.status != 0) {
216         // Some error occurred while constructing the decNumber.
217         status = U_INTERNAL_PROGRAM_ERROR;
218     }
219 }
220 
normalize()221 void DecNum::normalize() {
222     uprv_decNumberReduce(fData, fData, &fContext);
223 }
224 
multiplyBy(const DecNum & rhs,UErrorCode & status)225 void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) {
226     uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);
227     if (fContext.status != 0) {
228         status = U_INTERNAL_PROGRAM_ERROR;
229     }
230 }
231 
divideBy(const DecNum & rhs,UErrorCode & status)232 void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
233     uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
234     if ((fContext.status & DEC_Inexact) != 0) {
235         // Ignore.
236     } else if (fContext.status != 0) {
237         status = U_INTERNAL_PROGRAM_ERROR;
238     }
239 }
240 
isNegative() const241 bool DecNum::isNegative() const {
242     return decNumberIsNegative(fData.getAlias());
243 }
244 
isZero() const245 bool DecNum::isZero() const {
246     return decNumberIsZero(fData.getAlias());
247 }
248 
isSpecial() const249 bool DecNum::isSpecial() const {
250     return decNumberIsSpecial(fData.getAlias());
251 }
252 
isInfinity() const253 bool DecNum::isInfinity() const {
254     return decNumberIsInfinite(fData.getAlias());
255 }
256 
isNaN() const257 bool DecNum::isNaN() const {
258     return decNumberIsNaN(fData.getAlias());
259 }
260 
toString(ByteSink & output,UErrorCode & status) const261 void DecNum::toString(ByteSink& output, UErrorCode& status) const {
262     if (U_FAILURE(status)) {
263         return;
264     }
265     // "string must be at least dn->digits+14 characters long"
266     int32_t minCapacity = fData.getAlias()->digits + 14;
267     MaybeStackArray<char, 30> buffer(minCapacity, status);
268     if (U_FAILURE(status)) {
269         return;
270     }
271     uprv_decNumberToString(fData, buffer.getAlias());
272     output.Append(buffer.getAlias(), static_cast<int32_t>(uprv_strlen(buffer.getAlias())));
273 }
274 
275 #endif /* #if !UCONFIG_NO_FORMATTING */
276