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 ¤cy, 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 ¤cy) 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