number_utils.cpp 9.0 KB

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