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