Coverage Report

Created: 2025-09-05 07:16

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