• 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             U_ASSERT(false);
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     // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
185     if (decNumberIsSpecial(fData.getAlias())) {
186         status = U_UNSUPPORTED_ERROR;
187         return;
188     }
189 }
190 
191 void
setTo(const uint8_t * bcd,int32_t length,int32_t scale,bool isNegative,UErrorCode & status)192 DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) {
193     if (length > kDefaultDigits) {
194         fData.resize(length, 0);
195         fContext.digits = length;
196     } else {
197         fContext.digits = kDefaultDigits;
198     }
199 
200     // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
201     if (length < 1 || length > 999999999) {
202         // Too large for decNumber
203         status = U_UNSUPPORTED_ERROR;
204         return;
205     }
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     if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) {
212         // Too large for decNumber
213         status = U_UNSUPPORTED_ERROR;
214         return;
215     }
216 
217     fData.getAlias()->digits = length;
218     fData.getAlias()->exponent = scale;
219     fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);
220     uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));
221     if (fContext.status != 0) {
222         // Some error occurred while constructing the decNumber.
223         status = U_INTERNAL_PROGRAM_ERROR;
224     }
225 }
226 
normalize()227 void DecNum::normalize() {
228     uprv_decNumberReduce(fData, fData, &fContext);
229 }
230 
multiplyBy(const DecNum & rhs,UErrorCode & status)231 void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) {
232     uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);
233     if (fContext.status != 0) {
234         status = U_INTERNAL_PROGRAM_ERROR;
235     }
236 }
237 
divideBy(const DecNum & rhs,UErrorCode & status)238 void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
239     uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
240     if (fContext.status != 0) {
241         status = U_INTERNAL_PROGRAM_ERROR;
242     }
243 }
244 
isNegative() const245 bool DecNum::isNegative() const {
246     return decNumberIsNegative(fData.getAlias());
247 }
248 
isZero() const249 bool DecNum::isZero() const {
250     return decNumberIsZero(fData.getAlias());
251 }
252 
253 #endif /* #if !UCONFIG_NO_FORMATTING */
254