Coverage Report

Created: 2026-02-05 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/number_rounding.cpp
Line
Count
Source
1
// © 2017 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
#include "charstr.h"
9
#include "uassert.h"
10
#include "unicode/numberformatter.h"
11
#include "number_types.h"
12
#include "number_decimalquantity.h"
13
#include "double-conversion.h"
14
#include "number_roundingutils.h"
15
#include "number_skeletons.h"
16
#include "number_decnum.h"
17
#include "putilimp.h"
18
#include "string_segment.h"
19
20
using namespace icu;
21
using namespace icu::number;
22
using namespace icu::number::impl;
23
24
25
using double_conversion::DoubleToStringConverter;
26
using icu::StringSegment;
27
28
void number::impl::parseIncrementOption(const StringSegment &segment,
29
                                        Precision &outPrecision,
30
0
                                        UErrorCode &status) {
31
    // Need to do char <-> char16_t conversion...
32
0
    U_ASSERT(U_SUCCESS(status));
33
0
    CharString buffer;
34
0
    SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
35
36
    // Utilize DecimalQuantity/decNumber to parse this for us.
37
0
    DecimalQuantity dq;
38
0
    UErrorCode localStatus = U_ZERO_ERROR;
39
0
    dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
40
0
    if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) {
41
        // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
42
0
        status = U_NUMBER_SKELETON_SYNTAX_ERROR;
43
0
        return;
44
0
    }
45
    // Now we break apart the number into a mantissa and exponent (magnitude).
46
0
    int32_t magnitude = dq.adjustToZeroScale();
47
    // setToDecNumber drops trailing zeros, so we search for the '.' manually.
48
0
    for (int32_t i=0; i<buffer.length(); i++) {
49
0
        if (buffer[i] == '.') {
50
0
            int32_t newMagnitude = i - buffer.length() + 1;
51
0
            dq.adjustMagnitude(magnitude - newMagnitude);
52
0
            magnitude = newMagnitude;
53
0
            break;
54
0
        }
55
0
    }
56
0
    outPrecision = Precision::incrementExact(dq.toLong(), magnitude);
57
0
}
58
59
namespace {
60
61
1.68M
int32_t getRoundingMagnitudeFraction(int maxFrac) {
62
1.68M
    if (maxFrac == -1) {
63
77
        return INT32_MIN;
64
77
    }
65
1.68M
    return -maxFrac;
66
1.68M
}
67
68
4.95k
int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) {
69
4.95k
    if (maxSig == -1) {
70
495
        return INT32_MIN;
71
495
    }
72
4.46k
    int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
73
4.46k
    return magnitude - maxSig + 1;
74
4.95k
}
75
76
1.68M
int32_t getDisplayMagnitudeFraction(int minFrac) {
77
1.68M
    if (minFrac == 0) {
78
1.68M
        return INT32_MAX;
79
1.68M
    }
80
184
    return -minFrac;
81
1.68M
}
82
83
4.95k
int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
84
4.95k
    int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
85
4.95k
    return magnitude - minSig + 1;
86
4.95k
}
87
88
}
89
90
91
3.55k
MultiplierProducer::~MultiplierProducer() = default;
92
93
94
12.6k
Precision Precision::unlimited() {
95
12.6k
    return Precision(RND_NONE, {});
96
12.6k
}
97
98
2.86k
FractionPrecision Precision::integer() {
99
2.86k
    return constructFraction(0, 0);
100
2.86k
}
101
102
29.5k
FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
103
29.5k
    if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
104
29.5k
        return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
105
29.5k
    } else {
106
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
107
0
    }
108
29.5k
}
109
110
47
FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
111
47
    if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
112
38
        return constructFraction(minFractionPlaces, -1);
113
38
    } else {
114
9
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
115
9
    }
116
47
}
117
118
5.40k
FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
119
5.40k
    if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
120
5.40k
        return constructFraction(0, maxFractionPlaces);
121
5.40k
    } else {
122
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
123
0
    }
124
5.40k
}
125
126
300
FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
127
300
    if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
128
293
        minFractionPlaces <= maxFractionPlaces) {
129
293
        return constructFraction(minFractionPlaces, maxFractionPlaces);
130
293
    } else {
131
7
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
132
7
    }
133
300
}
134
135
0
Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
136
0
    if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
137
0
        return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
138
0
    } else {
139
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
140
0
    }
141
0
}
142
143
160
Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
144
160
    if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
145
154
        return constructSignificant(minSignificantDigits, -1);
146
154
    } else {
147
6
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
148
6
    }
149
160
}
150
151
0
Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
152
0
    if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
153
0
        return constructSignificant(1, maxSignificantDigits);
154
0
    } else {
155
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
156
0
    }
157
0
}
158
159
81
Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
160
81
    if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
161
79
        minSignificantDigits <= maxSignificantDigits) {
162
79
        return constructSignificant(minSignificantDigits, maxSignificantDigits);
163
79
    } else {
164
2
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
165
2
    }
166
81
}
167
168
27
Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const {
169
27
    Precision result(*this); // copy constructor
170
27
    result.fTrailingZeroDisplay = trailingZeroDisplay;
171
27
    return result;
172
27
}
173
174
15.6k
IncrementPrecision Precision::increment(double roundingIncrement) {
175
15.6k
    if (roundingIncrement > 0.0) {
176
15.6k
        DecimalQuantity dq;
177
15.6k
        dq.setToDouble(roundingIncrement);
178
15.6k
        dq.roundToInfinity();
179
15.6k
        int32_t magnitude = dq.adjustToZeroScale();
180
15.6k
        return constructIncrement(dq.toLong(), magnitude);
181
15.6k
    } else {
182
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
183
0
    }
184
15.6k
}
185
186
0
IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) {
187
0
    if (mantissa > 0.0) {
188
0
        return constructIncrement(mantissa, magnitude);
189
0
    } else {
190
0
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
191
0
    }
192
0
}
193
194
0
CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
195
0
    return constructCurrency(currencyUsage);
196
0
}
197
198
Precision FractionPrecision::withSignificantDigits(
199
        int32_t minSignificantDigits,
200
        int32_t maxSignificantDigits,
201
81
        UNumberRoundingPriority priority) const {
202
81
    if (fType == RND_ERROR) { return *this; } // no-op in error state
203
80
    if (minSignificantDigits >= 1 &&
204
80
            maxSignificantDigits >= minSignificantDigits &&
205
80
            maxSignificantDigits <= kMaxIntFracSig) {
206
58
        return constructFractionSignificant(
207
58
            *this,
208
58
            minSignificantDigits,
209
58
            maxSignificantDigits,
210
58
            priority,
211
58
            false);
212
58
    } else {
213
22
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
214
22
    }
215
80
}
216
217
2.87k
Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
218
2.87k
    if (fType == RND_ERROR) { return *this; } // no-op in error state
219
2.87k
    if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
220
2.87k
        return constructFractionSignificant(
221
2.87k
            *this,
222
2.87k
            1,
223
2.87k
            minSignificantDigits,
224
2.87k
            UNUM_ROUNDING_PRIORITY_RELAXED,
225
2.87k
            true);
226
2.87k
    } else {
227
2
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
228
2
    }
229
2.87k
}
230
231
58
Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
232
58
    if (fType == RND_ERROR) { return *this; } // no-op in error state
233
48
    if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
234
38
        return constructFractionSignificant(*this,
235
38
            1,
236
38
            maxSignificantDigits,
237
38
            UNUM_ROUNDING_PRIORITY_STRICT,
238
38
            true);
239
38
    } else {
240
10
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
241
10
    }
242
48
}
243
244
// Private method on base class
245
29.5k
Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
246
29.5k
    if (fType == RND_ERROR) { return *this; } // no-op in error state
247
29.5k
    U_ASSERT(fType == RND_CURRENCY);
248
29.5k
    const char16_t *isoCode = currency.getISOCurrency();
249
29.5k
    double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
250
29.5k
    int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
251
29.5k
            isoCode, fUnion.currencyUsage, &status);
252
29.5k
    Precision retval = (increment != 0.0)
253
29.5k
        ? Precision::increment(increment)
254
29.5k
        : static_cast<Precision>(Precision::fixedFraction(minMaxFrac));
255
29.5k
    retval.fTrailingZeroDisplay = fTrailingZeroDisplay;
256
29.5k
    return retval;
257
29.5k
}
258
259
// Public method on CurrencyPrecision subclass
260
0
Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
261
0
    UErrorCode localStatus = U_ZERO_ERROR;
262
0
    Precision result = Precision::withCurrency(currency, localStatus);
263
0
    if (U_FAILURE(localStatus)) {
264
0
        return {localStatus};
265
0
    }
266
0
    return result;
267
0
}
268
269
15.6k
Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
270
15.6k
    if (fType == RND_ERROR) { return *this; } // no-op in error state
271
15.6k
    if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
272
15.6k
        IncrementPrecision copy = *this;
273
15.6k
        copy.fUnion.increment.fMinFrac = minFrac;
274
15.6k
        return copy;
275
15.6k
    } else {
276
35
        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
277
35
    }
278
15.6k
}
279
280
515k
FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
281
515k
    FractionSignificantSettings settings{};
282
515k
    settings.fMinFrac = static_cast<digits_t>(minFrac);
283
515k
    settings.fMaxFrac = static_cast<digits_t>(maxFrac);
284
515k
    settings.fMinSig = -1;
285
515k
    settings.fMaxSig = -1;
286
515k
    PrecisionUnion union_{};
287
515k
    union_.fracSig = settings;
288
515k
    return {RND_FRACTION, union_};
289
515k
}
290
291
5.07k
Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
292
5.07k
    FractionSignificantSettings settings{};
293
5.07k
    settings.fMinFrac = -1;
294
5.07k
    settings.fMaxFrac = -1;
295
5.07k
    settings.fMinSig = static_cast<digits_t>(minSig);
296
5.07k
    settings.fMaxSig = static_cast<digits_t>(maxSig);
297
5.07k
    PrecisionUnion union_{};
298
5.07k
    union_.fracSig = settings;
299
5.07k
    return {RND_SIGNIFICANT, union_};
300
5.07k
}
301
302
Precision
303
Precision::constructFractionSignificant(
304
        const FractionPrecision &base,
305
        int32_t minSig,
306
        int32_t maxSig,
307
        UNumberRoundingPriority priority,
308
2.97k
        bool retain) {
309
2.97k
    FractionSignificantSettings settings = base.fUnion.fracSig;
310
2.97k
    settings.fMinSig = static_cast<digits_t>(minSig);
311
2.97k
    settings.fMaxSig = static_cast<digits_t>(maxSig);
312
2.97k
    settings.fPriority = priority;
313
2.97k
    settings.fRetain = retain;
314
2.97k
    PrecisionUnion union_{};
315
2.97k
    union_.fracSig = settings;
316
2.97k
    return {RND_FRACTION_SIGNIFICANT, union_};
317
2.97k
}
318
319
15.6k
IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) {
320
15.6k
    IncrementSettings settings{};
321
    // Note: For number formatting, fIncrement is used for RND_INCREMENT but not
322
    // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all
323
    // three when constructing a skeleton.
324
15.6k
    settings.fIncrement = increment;
325
15.6k
    settings.fIncrementMagnitude = magnitude;
326
15.6k
    settings.fMinFrac = magnitude > 0 ? 0 : -magnitude;
327
15.6k
    PrecisionUnion union_{};
328
15.6k
    union_.increment = settings;
329
15.6k
    if (increment == 1) {
330
        // NOTE: In C++, we must return the correct value type with the correct union.
331
        // It would be invalid to return a RND_FRACTION here because the methods on the
332
        // IncrementPrecision type assume that the union is backed by increment data.
333
1.08k
        return {RND_INCREMENT_ONE, union_};
334
14.6k
    } else if (increment == 5) {
335
259
        return {RND_INCREMENT_FIVE, union_};
336
14.3k
    } else {
337
14.3k
        return {RND_INCREMENT, union_};
338
14.3k
    }
339
15.6k
}
340
341
29.5k
CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
342
29.5k
    PrecisionUnion union_{};
343
29.5k
    union_.currencyUsage = usage;
344
29.5k
    return {RND_CURRENCY, union_};
345
29.5k
}
346
347
348
RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
349
                           const CurrencyUnit& currency, UErrorCode& status)
350
11.5k
        : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
351
11.5k
    if (precision.fType == Precision::RND_CURRENCY) {
352
0
        fPrecision = precision.withCurrency(currency, status);
353
0
    }
354
11.5k
}
355
356
3.55k
RoundingImpl RoundingImpl::passThrough() {
357
3.55k
    return {};
358
3.55k
}
359
360
0
bool RoundingImpl::isSignificantDigits() const {
361
0
    return fPrecision.fType == Precision::RND_SIGNIFICANT;
362
0
}
363
364
int32_t
365
RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
366
3.47k
                                  UErrorCode &status) {
367
    // Do not call this method with zero, NaN, or infinity.
368
3.47k
    U_ASSERT(!input.isZeroish());
369
370
    // Perform the first attempt at rounding.
371
3.47k
    int magnitude = input.getMagnitude();
372
3.47k
    int multiplier = producer.getMultiplier(magnitude);
373
3.47k
    input.adjustMagnitude(multiplier);
374
3.47k
    apply(input, status);
375
376
    // If the number rounded to zero, exit.
377
3.47k
    if (input.isZeroish() || U_FAILURE(status)) {
378
24
        return multiplier;
379
24
    }
380
381
    // If the new magnitude after rounding is the same as it was before rounding, then we are done.
382
    // This case applies to most numbers.
383
3.45k
    if (input.getMagnitude() == magnitude + multiplier) {
384
3.34k
        return multiplier;
385
3.34k
    }
386
387
    // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
388
    // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
389
    // we do not need to make any more adjustments.
390
109
    int _multiplier = producer.getMultiplier(magnitude + 1);
391
109
    if (multiplier == _multiplier) {
392
65
        return multiplier;
393
65
    }
394
395
    // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
396
    // Fix the magnitude and re-apply the rounding strategy.
397
44
    input.adjustMagnitude(_multiplier - multiplier);
398
44
    apply(input, status);
399
44
    return _multiplier;
400
109
}
401
402
/** This is the method that contains the actual rounding logic. */
403
1.69M
void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
404
1.69M
    if (U_FAILURE(status)) {
405
0
        return;
406
0
    }
407
1.69M
    if (fPassThrough) {
408
3.55k
        return;
409
3.55k
    }
410
1.68M
    int32_t resolvedMinFraction = 0;
411
1.68M
    switch (fPrecision.fType) {
412
0
        case Precision::RND_BOGUS:
413
0
        case Precision::RND_ERROR:
414
            // Errors should be caught before the apply() method is called
415
0
            status = U_INTERNAL_PROGRAM_ERROR;
416
0
            break;
417
418
403
        case Precision::RND_NONE:
419
403
            value.roundToInfinity();
420
403
            break;
421
422
1.68M
        case Precision::RND_FRACTION:
423
1.68M
            value.roundToMagnitude(
424
1.68M
                    getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
425
1.68M
                    fRoundingMode,
426
1.68M
                    status);
427
1.68M
            resolvedMinFraction =
428
1.68M
                    uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac));
429
1.68M
            break;
430
431
689
        case Precision::RND_SIGNIFICANT:
432
689
            value.roundToMagnitude(
433
689
                    getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
434
689
                    fRoundingMode,
435
689
                    status);
436
689
            resolvedMinFraction =
437
689
                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig));
438
            // Make sure that digits are displayed on zero.
439
689
            if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
440
17
                value.increaseMinIntegerTo(1);
441
17
            }
442
689
            break;
443
444
4.26k
        case Precision::RND_FRACTION_SIGNIFICANT: {
445
            // From ECMA-402:
446
            /*
447
            Let sResult be ToRawPrecision(...).
448
            Let fResult be ToRawFixed(...).
449
            If intlObj.[[RoundingType]] is morePrecision, then
450
                If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
451
                    Let result be sResult.
452
                Else,
453
                    Let result be fResult.
454
            Else,
455
                Assert: intlObj.[[RoundingType]] is lessPrecision.
456
                If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
457
                    Let result be fResult.
458
                Else,
459
                    Let result be sResult.
460
            */
461
462
4.26k
            int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
463
4.26k
            int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig);
464
4.26k
            int32_t roundingMag;
465
4.26k
            if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
466
4.09k
                roundingMag = uprv_min(roundingMag1, roundingMag2);
467
4.09k
            } else {
468
168
                roundingMag = uprv_max(roundingMag1, roundingMag2);
469
168
            }
470
4.26k
            if (!value.isZeroish()) {
471
4.16k
                int32_t upperMag = value.getMagnitude();
472
4.16k
                value.roundToMagnitude(roundingMag, fRoundingMode, status);
473
4.16k
                if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) {
474
                    // roundingMag2 needs to be the magnitude after rounding
475
12
                    roundingMag2 += 1;
476
12
                }
477
4.16k
            }
478
479
4.26k
            int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
480
4.26k
            int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig);
481
4.26k
            int32_t displayMag;
482
4.26k
            if (fPrecision.fUnion.fracSig.fRetain) {
483
                // withMinDigits + withMaxDigits
484
4.13k
                displayMag = uprv_min(displayMag1, displayMag2);
485
4.13k
            } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
486
38
                if (roundingMag2 <= roundingMag1) {
487
15
                    displayMag = displayMag2;
488
23
                } else {
489
23
                    displayMag = displayMag1;
490
23
                }
491
93
            } else {
492
93
                U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT);
493
93
                if (roundingMag2 <= roundingMag1) {
494
46
                    displayMag = displayMag1;
495
47
                } else {
496
47
                    displayMag = displayMag2;
497
47
                }
498
93
            }
499
4.26k
            resolvedMinFraction = uprv_max(0, -displayMag);
500
501
4.26k
            break;
502
0
        }
503
504
0
        case Precision::RND_INCREMENT:
505
0
            value.roundToIncrement(
506
0
                    fPrecision.fUnion.increment.fIncrement,
507
0
                    fPrecision.fUnion.increment.fIncrementMagnitude,
508
0
                    fRoundingMode,
509
0
                    status);
510
0
            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
511
0
            break;
512
513
0
        case Precision::RND_INCREMENT_ONE:
514
0
            value.roundToMagnitude(
515
0
                    fPrecision.fUnion.increment.fIncrementMagnitude,
516
0
                    fRoundingMode,
517
0
                    status);
518
0
            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
519
0
            break;
520
521
0
        case Precision::RND_INCREMENT_FIVE:
522
0
            value.roundToNickel(
523
0
                    fPrecision.fUnion.increment.fIncrementMagnitude,
524
0
                    fRoundingMode,
525
0
                    status);
526
0
            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
527
0
            break;
528
529
0
        case Precision::RND_CURRENCY:
530
            // Call .withCurrency() before .apply()!
531
0
            UPRV_UNREACHABLE_EXIT;
532
533
0
        default:
534
0
            UPRV_UNREACHABLE_EXIT;
535
1.68M
    }
536
537
1.68M
    if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO ||
538
            // PLURAL_OPERAND_T returns fraction digits as an integer
539
1.68M
            value.getPluralOperand(PLURAL_OPERAND_T) != 0) {
540
1.68M
        value.setMinFraction(resolvedMinFraction);
541
1.68M
    }
542
1.68M
}
543
544
0
void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
545
    // This method is intended for the one specific purpose of helping print "00.000E0".
546
    // Question: Is it useful to look at trailingZeroDisplay here?
547
0
    U_ASSERT(isSignificantDigits());
548
0
    U_ASSERT(value.isZeroish());
549
0
    value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
550
0
}
551
552
#endif /* #if !UCONFIG_NO_FORMATTING */