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(ures_open(nullptr, locale
.getName(), &status
));
76 if (U_FAILURE(status
)) { return u
""; }
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
""; }
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
""; }
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?)
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);
108 status
= U_MEMORY_ALLOCATION_ERROR
;
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());
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
);
127 void DecNum::setTo(const char* str
, UErrorCode
& status
) {
128 _setTo(str
, static_cast<int32_t>(uprv_strlen(str
)), 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
;
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
144 DoubleToStringConverter::DoubleToAscii(
146 DoubleToStringConverter::DtoaMode::SHORTEST
,
155 // Read initial result as a string.
156 _setTo(buffer
, length
, status
);
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);
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
;
168 fContext
.digits
= kDefaultDigits
;
171 static_assert(DECDPUN
== 1, "Assumes that DECDPUN is set to 1");
172 uprv_decNumberFromString(fData
.getAlias(), str
, &fContext
);
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
;
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
;
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
;
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
;
197 fContext
.digits
= kDefaultDigits
;
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
;
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
;
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
;
227 void DecNum::normalize() {
228 uprv_decNumberReduce(fData
, fData
, &fContext
);
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
;
238 void DecNum::divideBy(const DecNum
& rhs
, UErrorCode
& status
) {
239 uprv_decNumberDivide(fData
, fData
, rhs
.fData
, &fContext
);
240 if ((fContext
.status
& DEC_Inexact
) != 0) {
242 } else if (fContext
.status
!= 0) {
243 status
= U_INTERNAL_PROGRAM_ERROR
;
247 bool DecNum::isNegative() const {
248 return decNumberIsNegative(fData
.getAlias());
251 bool DecNum::isZero() const {
252 return decNumberIsZero(fData
.getAlias());
255 #endif /* #if !UCONFIG_NO_FORMATTING */