• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
parseIncrementOption(const StringSegment & segment,Precision & outPrecision,UErrorCode & status)28 void number::impl::parseIncrementOption(const StringSegment &segment,
29                                         Precision &outPrecision,
30                                         UErrorCode &status) {
31     // Need to do char <-> UChar conversion...
32     U_ASSERT(U_SUCCESS(status));
33     CharString buffer;
34     SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
35 
36     // Utilize DecimalQuantity/decNumber to parse this for us.
37     DecimalQuantity dq;
38     UErrorCode localStatus = U_ZERO_ERROR;
39     dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
40     if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) {
41         // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
42         status = U_NUMBER_SKELETON_SYNTAX_ERROR;
43         return;
44     }
45     // Now we break apart the number into a mantissa and exponent (magnitude).
46     int32_t magnitude = dq.adjustToZeroScale();
47     // setToDecNumber drops trailing zeros, so we search for the '.' manually.
48     for (int32_t i=0; i<buffer.length(); i++) {
49         if (buffer[i] == '.') {
50             int32_t newMagnitude = i - buffer.length() + 1;
51             dq.adjustMagnitude(magnitude - newMagnitude);
52             magnitude = newMagnitude;
53             break;
54         }
55     }
56     outPrecision = Precision::incrementExact(dq.toLong(), magnitude);
57 }
58 
59 namespace {
60 
getRoundingMagnitudeFraction(int maxFrac)61 int32_t getRoundingMagnitudeFraction(int maxFrac) {
62     if (maxFrac == -1) {
63         return INT32_MIN;
64     }
65     return -maxFrac;
66 }
67 
getRoundingMagnitudeSignificant(const DecimalQuantity & value,int maxSig)68 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) {
69     if (maxSig == -1) {
70         return INT32_MIN;
71     }
72     int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
73     return magnitude - maxSig + 1;
74 }
75 
getDisplayMagnitudeFraction(int minFrac)76 int32_t getDisplayMagnitudeFraction(int minFrac) {
77     if (minFrac == 0) {
78         return INT32_MAX;
79     }
80     return -minFrac;
81 }
82 
getDisplayMagnitudeSignificant(const DecimalQuantity & value,int minSig)83 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
84     int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
85     return magnitude - minSig + 1;
86 }
87 
88 }
89 
90 
91 MultiplierProducer::~MultiplierProducer() = default;
92 
93 
unlimited()94 Precision Precision::unlimited() {
95     return Precision(RND_NONE, {});
96 }
97 
integer()98 FractionPrecision Precision::integer() {
99     return constructFraction(0, 0);
100 }
101 
fixedFraction(int32_t minMaxFractionPlaces)102 FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
103     if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
104         return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
105     } else {
106         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
107     }
108 }
109 
minFraction(int32_t minFractionPlaces)110 FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
111     if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
112         return constructFraction(minFractionPlaces, -1);
113     } else {
114         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
115     }
116 }
117 
maxFraction(int32_t maxFractionPlaces)118 FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
119     if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
120         return constructFraction(0, maxFractionPlaces);
121     } else {
122         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
123     }
124 }
125 
minMaxFraction(int32_t minFractionPlaces,int32_t maxFractionPlaces)126 FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
127     if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
128         minFractionPlaces <= maxFractionPlaces) {
129         return constructFraction(minFractionPlaces, maxFractionPlaces);
130     } else {
131         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
132     }
133 }
134 
fixedSignificantDigits(int32_t minMaxSignificantDigits)135 Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
136     if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
137         return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
138     } else {
139         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
140     }
141 }
142 
minSignificantDigits(int32_t minSignificantDigits)143 Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
144     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
145         return constructSignificant(minSignificantDigits, -1);
146     } else {
147         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
148     }
149 }
150 
maxSignificantDigits(int32_t maxSignificantDigits)151 Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
152     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
153         return constructSignificant(1, maxSignificantDigits);
154     } else {
155         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
156     }
157 }
158 
minMaxSignificantDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits)159 Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
160     if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
161         minSignificantDigits <= maxSignificantDigits) {
162         return constructSignificant(minSignificantDigits, maxSignificantDigits);
163     } else {
164         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
165     }
166 }
167 
trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const168 Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const {
169     Precision result(*this); // copy constructor
170     result.fTrailingZeroDisplay = trailingZeroDisplay;
171     return result;
172 }
173 
increment(double roundingIncrement)174 IncrementPrecision Precision::increment(double roundingIncrement) {
175     if (roundingIncrement > 0.0) {
176         DecimalQuantity dq;
177         dq.setToDouble(roundingIncrement);
178         dq.roundToInfinity();
179         int32_t magnitude = dq.adjustToZeroScale();
180         return constructIncrement(dq.toLong(), magnitude);
181     } else {
182         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
183     }
184 }
185 
incrementExact(uint64_t mantissa,int16_t magnitude)186 IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) {
187     if (mantissa > 0.0) {
188         return constructIncrement(mantissa, magnitude);
189     } else {
190         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
191     }
192 }
193 
currency(UCurrencyUsage currencyUsage)194 CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
195     return constructCurrency(currencyUsage);
196 }
197 
withSignificantDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits,UNumberRoundingPriority priority) const198 Precision FractionPrecision::withSignificantDigits(
199         int32_t minSignificantDigits,
200         int32_t maxSignificantDigits,
201         UNumberRoundingPriority priority) const {
202     if (fType == RND_ERROR) { return *this; } // no-op in error state
203     if (minSignificantDigits >= 1 &&
204             maxSignificantDigits >= minSignificantDigits &&
205             maxSignificantDigits <= kMaxIntFracSig) {
206         return constructFractionSignificant(
207             *this,
208             minSignificantDigits,
209             maxSignificantDigits,
210             priority,
211             false);
212     } else {
213         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
214     }
215 }
216 
withMinDigits(int32_t minSignificantDigits) const217 Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
218     if (fType == RND_ERROR) { return *this; } // no-op in error state
219     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
220         return constructFractionSignificant(
221             *this,
222             1,
223             minSignificantDigits,
224             UNUM_ROUNDING_PRIORITY_RELAXED,
225             true);
226     } else {
227         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
228     }
229 }
230 
withMaxDigits(int32_t maxSignificantDigits) const231 Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
232     if (fType == RND_ERROR) { return *this; } // no-op in error state
233     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
234         return constructFractionSignificant(*this,
235             1,
236             maxSignificantDigits,
237             UNUM_ROUNDING_PRIORITY_STRICT,
238             true);
239     } else {
240         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
241     }
242 }
243 
244 // Private method on base class
withCurrency(const CurrencyUnit & currency,UErrorCode & status) const245 Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
246     if (fType == RND_ERROR) { return *this; } // no-op in error state
247     U_ASSERT(fType == RND_CURRENCY);
248     const char16_t *isoCode = currency.getISOCurrency();
249     double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
250     int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
251             isoCode, fUnion.currencyUsage, &status);
252     Precision retval = (increment != 0.0)
253         ? Precision::increment(increment)
254         : static_cast<Precision>(Precision::fixedFraction(minMaxFrac));
255     retval.fTrailingZeroDisplay = fTrailingZeroDisplay;
256     return retval;
257 }
258 
259 // Public method on CurrencyPrecision subclass
withCurrency(const CurrencyUnit & currency) const260 Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
261     UErrorCode localStatus = U_ZERO_ERROR;
262     Precision result = Precision::withCurrency(currency, localStatus);
263     if (U_FAILURE(localStatus)) {
264         return {localStatus};
265     }
266     return result;
267 }
268 
withMinFraction(int32_t minFrac) const269 Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
270     if (fType == RND_ERROR) { return *this; } // no-op in error state
271     if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
272         IncrementPrecision copy = *this;
273         copy.fUnion.increment.fMinFrac = minFrac;
274         return copy;
275     } else {
276         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
277     }
278 }
279 
constructFraction(int32_t minFrac,int32_t maxFrac)280 FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
281     FractionSignificantSettings settings;
282     settings.fMinFrac = static_cast<digits_t>(minFrac);
283     settings.fMaxFrac = static_cast<digits_t>(maxFrac);
284     settings.fMinSig = -1;
285     settings.fMaxSig = -1;
286     PrecisionUnion union_;
287     union_.fracSig = settings;
288     return {RND_FRACTION, union_};
289 }
290 
constructSignificant(int32_t minSig,int32_t maxSig)291 Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
292     FractionSignificantSettings settings;
293     settings.fMinFrac = -1;
294     settings.fMaxFrac = -1;
295     settings.fMinSig = static_cast<digits_t>(minSig);
296     settings.fMaxSig = static_cast<digits_t>(maxSig);
297     PrecisionUnion union_;
298     union_.fracSig = settings;
299     return {RND_SIGNIFICANT, union_};
300 }
301 
302 Precision
constructFractionSignificant(const FractionPrecision & base,int32_t minSig,int32_t maxSig,UNumberRoundingPriority priority,bool retain)303 Precision::constructFractionSignificant(
304         const FractionPrecision &base,
305         int32_t minSig,
306         int32_t maxSig,
307         UNumberRoundingPriority priority,
308         bool retain) {
309     FractionSignificantSettings settings = base.fUnion.fracSig;
310     settings.fMinSig = static_cast<digits_t>(minSig);
311     settings.fMaxSig = static_cast<digits_t>(maxSig);
312     settings.fPriority = priority;
313     settings.fRetain = retain;
314     PrecisionUnion union_;
315     union_.fracSig = settings;
316     return {RND_FRACTION_SIGNIFICANT, union_};
317 }
318 
constructIncrement(uint64_t increment,digits_t magnitude)319 IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) {
320     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     settings.fIncrement = increment;
325     settings.fIncrementMagnitude = magnitude;
326     settings.fMinFrac = magnitude > 0 ? 0 : -magnitude;
327     PrecisionUnion union_;
328     union_.increment = settings;
329     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         return {RND_INCREMENT_ONE, union_};
334     } else if (increment == 5) {
335         return {RND_INCREMENT_FIVE, union_};
336     } else {
337         return {RND_INCREMENT, union_};
338     }
339 }
340 
constructCurrency(UCurrencyUsage usage)341 CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
342     PrecisionUnion union_;
343     union_.currencyUsage = usage;
344     return {RND_CURRENCY, union_};
345 }
346 
347 
RoundingImpl(const Precision & precision,UNumberFormatRoundingMode roundingMode,const CurrencyUnit & currency,UErrorCode & status)348 RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
349                            const CurrencyUnit& currency, UErrorCode& status)
350         : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
351     if (precision.fType == Precision::RND_CURRENCY) {
352         fPrecision = precision.withCurrency(currency, status);
353     }
354 }
355 
passThrough()356 RoundingImpl RoundingImpl::passThrough() {
357     return {};
358 }
359 
isSignificantDigits() const360 bool RoundingImpl::isSignificantDigits() const {
361     return fPrecision.fType == Precision::RND_SIGNIFICANT;
362 }
363 
364 int32_t
chooseMultiplierAndApply(impl::DecimalQuantity & input,const impl::MultiplierProducer & producer,UErrorCode & status)365 RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
366                                   UErrorCode &status) {
367     // Do not call this method with zero, NaN, or infinity.
368     U_ASSERT(!input.isZeroish());
369 
370     // Perform the first attempt at rounding.
371     int magnitude = input.getMagnitude();
372     int multiplier = producer.getMultiplier(magnitude);
373     input.adjustMagnitude(multiplier);
374     apply(input, status);
375 
376     // If the number rounded to zero, exit.
377     if (input.isZeroish() || U_FAILURE(status)) {
378         return multiplier;
379     }
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     if (input.getMagnitude() == magnitude + multiplier) {
384         return multiplier;
385     }
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     int _multiplier = producer.getMultiplier(magnitude + 1);
391     if (multiplier == _multiplier) {
392         return multiplier;
393     }
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     input.adjustMagnitude(_multiplier - multiplier);
398     apply(input, status);
399     return _multiplier;
400 }
401 
402 /** This is the method that contains the actual rounding logic. */
apply(impl::DecimalQuantity & value,UErrorCode & status) const403 void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
404     if (U_FAILURE(status)) {
405         return;
406     }
407     if (fPassThrough) {
408         return;
409     }
410     int32_t resolvedMinFraction = 0;
411     switch (fPrecision.fType) {
412         case Precision::RND_BOGUS:
413         case Precision::RND_ERROR:
414             // Errors should be caught before the apply() method is called
415             status = U_INTERNAL_PROGRAM_ERROR;
416             break;
417 
418         case Precision::RND_NONE:
419             value.roundToInfinity();
420             break;
421 
422         case Precision::RND_FRACTION:
423             value.roundToMagnitude(
424                     getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
425                     fRoundingMode,
426                     status);
427             resolvedMinFraction =
428                     uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac));
429             break;
430 
431         case Precision::RND_SIGNIFICANT:
432             value.roundToMagnitude(
433                     getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
434                     fRoundingMode,
435                     status);
436             resolvedMinFraction =
437                     uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig));
438             // Make sure that digits are displayed on zero.
439             if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
440                 value.setMinInteger(1);
441             }
442             break;
443 
444         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             int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
463             int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig);
464             int32_t roundingMag;
465             if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
466                 roundingMag = uprv_min(roundingMag1, roundingMag2);
467             } else {
468                 roundingMag = uprv_max(roundingMag1, roundingMag2);
469             }
470             if (!value.isZeroish()) {
471                 int32_t upperMag = value.getMagnitude();
472                 value.roundToMagnitude(roundingMag, fRoundingMode, status);
473                 if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) {
474                     // roundingMag2 needs to be the magnitude after rounding
475                     roundingMag2 += 1;
476                 }
477             }
478 
479             int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
480             int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig);
481             int32_t displayMag;
482             if (fPrecision.fUnion.fracSig.fRetain) {
483                 // withMinDigits + withMaxDigits
484                 displayMag = uprv_min(displayMag1, displayMag2);
485             } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
486                 if (roundingMag2 <= roundingMag1) {
487                     displayMag = displayMag2;
488                 } else {
489                     displayMag = displayMag1;
490                 }
491             } else {
492                 U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT);
493                 if (roundingMag2 <= roundingMag1) {
494                     displayMag = displayMag1;
495                 } else {
496                     displayMag = displayMag2;
497                 }
498             }
499             resolvedMinFraction = uprv_max(0, -displayMag);
500 
501             break;
502         }
503 
504         case Precision::RND_INCREMENT:
505             value.roundToIncrement(
506                     fPrecision.fUnion.increment.fIncrement,
507                     fPrecision.fUnion.increment.fIncrementMagnitude,
508                     fRoundingMode,
509                     status);
510             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
511             break;
512 
513         case Precision::RND_INCREMENT_ONE:
514             value.roundToMagnitude(
515                     fPrecision.fUnion.increment.fIncrementMagnitude,
516                     fRoundingMode,
517                     status);
518             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
519             break;
520 
521         case Precision::RND_INCREMENT_FIVE:
522             value.roundToNickel(
523                     fPrecision.fUnion.increment.fIncrementMagnitude,
524                     fRoundingMode,
525                     status);
526             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
527             break;
528 
529         case Precision::RND_CURRENCY:
530             // Call .withCurrency() before .apply()!
531             UPRV_UNREACHABLE_EXIT;
532 
533         default:
534             UPRV_UNREACHABLE_EXIT;
535     }
536 
537     if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO ||
538             // PLURAL_OPERAND_T returns fraction digits as an integer
539             value.getPluralOperand(PLURAL_OPERAND_T) != 0) {
540         value.setMinFraction(resolvedMinFraction);
541     }
542 }
543 
apply(impl::DecimalQuantity & value,int32_t minInt,UErrorCode)544 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     U_ASSERT(isSignificantDigits());
548     U_ASSERT(value.isZeroish());
549     value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
550 }
551 
552 #endif /* #if !UCONFIG_NO_FORMATTING */
553