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 "putilimp.h"
17 #include "string_segment.h"
18 
19 using namespace icu;
20 using namespace icu::number;
21 using namespace icu::number::impl;
22 
23 
24 using double_conversion::DoubleToStringConverter;
25 using icu::StringSegment;
26 
parseIncrementOption(const StringSegment & segment,Precision & outPrecision,UErrorCode & status)27 void number::impl::parseIncrementOption(const StringSegment &segment,
28                                         Precision &outPrecision,
29                                         UErrorCode &status) {
30     // Need to do char <-> UChar conversion...
31     U_ASSERT(U_SUCCESS(status));
32     CharString buffer;
33     SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
34 
35     // Utilize DecimalQuantity/decNumber to parse this for us.
36     DecimalQuantity dq;
37     UErrorCode localStatus = U_ZERO_ERROR;
38     dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
39     if (U_FAILURE(localStatus)) {
40         // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
41         status = U_NUMBER_SKELETON_SYNTAX_ERROR;
42         return;
43     }
44     double increment = dq.toDouble();
45 
46     // We also need to figure out how many digits. Do a brute force string operation.
47     int decimalOffset = 0;
48     while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') {
49         decimalOffset++;
50     }
51     if (decimalOffset == segment.length()) {
52         outPrecision = Precision::increment(increment);
53     } else {
54         int32_t fractionLength = segment.length() - decimalOffset - 1;
55         outPrecision = Precision::increment(increment).withMinFraction(fractionLength);
56     }
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 
doubleFractionLength(double input,int8_t * singleDigit)94 digits_t roundingutils::doubleFractionLength(double input, int8_t* singleDigit) {
95     char buffer[DoubleToStringConverter::kBase10MaximalLength + 1];
96     bool sign; // unused; always positive
97     int32_t length;
98     int32_t point;
99     DoubleToStringConverter::DoubleToAscii(
100             input,
101             DoubleToStringConverter::DtoaMode::SHORTEST,
102             0,
103             buffer,
104             sizeof(buffer),
105             &sign,
106             &length,
107             &point
108     );
109 
110     if (singleDigit == nullptr) {
111         // no-op
112     } else if (length == 1) {
113         *singleDigit = buffer[0] - '0';
114     } else {
115         *singleDigit = -1;
116     }
117 
118     return static_cast<digits_t>(length - point);
119 }
120 
121 
unlimited()122 Precision Precision::unlimited() {
123     return Precision(RND_NONE, {});
124 }
125 
integer()126 FractionPrecision Precision::integer() {
127     return constructFraction(0, 0);
128 }
129 
fixedFraction(int32_t minMaxFractionPlaces)130 FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
131     if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
132         return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
133     } else {
134         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
135     }
136 }
137 
minFraction(int32_t minFractionPlaces)138 FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
139     if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
140         return constructFraction(minFractionPlaces, -1);
141     } else {
142         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
143     }
144 }
145 
maxFraction(int32_t maxFractionPlaces)146 FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
147     if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
148         return constructFraction(0, maxFractionPlaces);
149     } else {
150         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
151     }
152 }
153 
minMaxFraction(int32_t minFractionPlaces,int32_t maxFractionPlaces)154 FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
155     if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
156         minFractionPlaces <= maxFractionPlaces) {
157         return constructFraction(minFractionPlaces, maxFractionPlaces);
158     } else {
159         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
160     }
161 }
162 
fixedSignificantDigits(int32_t minMaxSignificantDigits)163 Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
164     if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
165         return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
166     } else {
167         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
168     }
169 }
170 
minSignificantDigits(int32_t minSignificantDigits)171 Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
172     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
173         return constructSignificant(minSignificantDigits, -1);
174     } else {
175         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
176     }
177 }
178 
maxSignificantDigits(int32_t maxSignificantDigits)179 Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
180     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
181         return constructSignificant(1, maxSignificantDigits);
182     } else {
183         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
184     }
185 }
186 
minMaxSignificantDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits)187 Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
188     if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
189         minSignificantDigits <= maxSignificantDigits) {
190         return constructSignificant(minSignificantDigits, maxSignificantDigits);
191     } else {
192         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
193     }
194 }
195 
trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const196 Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const {
197     Precision result(*this); // copy constructor
198     result.fTrailingZeroDisplay = trailingZeroDisplay;
199     return result;
200 }
201 
increment(double roundingIncrement)202 IncrementPrecision Precision::increment(double roundingIncrement) {
203     if (roundingIncrement > 0.0) {
204         return constructIncrement(roundingIncrement, 0);
205     } else {
206         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
207     }
208 }
209 
currency(UCurrencyUsage currencyUsage)210 CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
211     return constructCurrency(currencyUsage);
212 }
213 
withSignificantDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits,UNumberRoundingPriority priority) const214 Precision FractionPrecision::withSignificantDigits(
215         int32_t minSignificantDigits,
216         int32_t maxSignificantDigits,
217         UNumberRoundingPriority priority) const {
218     if (fType == RND_ERROR) { return *this; } // no-op in error state
219     if (minSignificantDigits >= 1 &&
220             maxSignificantDigits >= minSignificantDigits &&
221             maxSignificantDigits <= kMaxIntFracSig) {
222         return constructFractionSignificant(
223             *this,
224             minSignificantDigits,
225             maxSignificantDigits,
226             priority);
227     } else {
228         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
229     }
230 }
231 
withMinDigits(int32_t minSignificantDigits) const232 Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
233     if (fType == RND_ERROR) { return *this; } // no-op in error state
234     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
235         return constructFractionSignificant(
236             *this,
237             1,
238             minSignificantDigits,
239             UNUM_ROUNDING_PRIORITY_RELAXED);
240     } else {
241         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
242     }
243 }
244 
withMaxDigits(int32_t maxSignificantDigits) const245 Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
246     if (fType == RND_ERROR) { return *this; } // no-op in error state
247     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
248         return constructFractionSignificant(*this,
249             1,
250             maxSignificantDigits,
251             UNUM_ROUNDING_PRIORITY_STRICT);
252     } else {
253         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
254     }
255 }
256 
257 // Private method on base class
withCurrency(const CurrencyUnit & currency,UErrorCode & status) const258 Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
259     if (fType == RND_ERROR) { return *this; } // no-op in error state
260     U_ASSERT(fType == RND_CURRENCY);
261     const char16_t *isoCode = currency.getISOCurrency();
262     double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
263     int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
264             isoCode, fUnion.currencyUsage, &status);
265     Precision retval = (increment != 0.0)
266         ? static_cast<Precision>(constructIncrement(increment, minMaxFrac))
267         : static_cast<Precision>(constructFraction(minMaxFrac, minMaxFrac));
268     retval.fTrailingZeroDisplay = fTrailingZeroDisplay;
269     return retval;
270 }
271 
272 // Public method on CurrencyPrecision subclass
withCurrency(const CurrencyUnit & currency) const273 Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
274     UErrorCode localStatus = U_ZERO_ERROR;
275     Precision result = Precision::withCurrency(currency, localStatus);
276     if (U_FAILURE(localStatus)) {
277         return {localStatus};
278     }
279     return result;
280 }
281 
withMinFraction(int32_t minFrac) const282 Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
283     if (fType == RND_ERROR) { return *this; } // no-op in error state
284     if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
285         return constructIncrement(fUnion.increment.fIncrement, minFrac);
286     } else {
287         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
288     }
289 }
290 
constructFraction(int32_t minFrac,int32_t maxFrac)291 FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
292     FractionSignificantSettings settings;
293     settings.fMinFrac = static_cast<digits_t>(minFrac);
294     settings.fMaxFrac = static_cast<digits_t>(maxFrac);
295     settings.fMinSig = -1;
296     settings.fMaxSig = -1;
297 #ifdef U_STRINGI_PATCHES
298     settings.fPriority = UNUM_ROUNDING_PRIORITY_RELAXED;
299 #endif
300     PrecisionUnion union_;
301     union_.fracSig = settings;
302     return {RND_FRACTION, union_};
303 }
304 
constructSignificant(int32_t minSig,int32_t maxSig)305 Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
306     FractionSignificantSettings settings;
307     settings.fMinFrac = -1;
308     settings.fMaxFrac = -1;
309     settings.fMinSig = static_cast<digits_t>(minSig);
310     settings.fMaxSig = static_cast<digits_t>(maxSig);
311 #ifdef U_STRINGI_PATCHES
312     settings.fPriority = UNUM_ROUNDING_PRIORITY_RELAXED;
313 #endif
314     PrecisionUnion union_;
315     union_.fracSig = settings;
316     return {RND_SIGNIFICANT, union_};
317 }
318 
319 Precision
constructFractionSignificant(const FractionPrecision & base,int32_t minSig,int32_t maxSig,UNumberRoundingPriority priority)320 Precision::constructFractionSignificant(
321         const FractionPrecision &base,
322         int32_t minSig,
323         int32_t maxSig,
324         UNumberRoundingPriority priority) {
325     FractionSignificantSettings settings = base.fUnion.fracSig;
326     settings.fMinSig = static_cast<digits_t>(minSig);
327     settings.fMaxSig = static_cast<digits_t>(maxSig);
328     settings.fPriority = priority;
329     PrecisionUnion union_;
330     union_.fracSig = settings;
331     return {RND_FRACTION_SIGNIFICANT, union_};
332 }
333 
constructIncrement(double increment,int32_t minFrac)334 IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
335     IncrementSettings settings;
336     // Note: For number formatting, fIncrement is used for RND_INCREMENT but not
337     // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all
338     // three when constructing a skeleton.
339     settings.fIncrement = increment;
340     settings.fMinFrac = static_cast<digits_t>(minFrac);
341     // One of the few pre-computed quantities:
342     // Note: it is possible for minFrac to be more than maxFrac... (misleading)
343     int8_t singleDigit;
344     settings.fMaxFrac = roundingutils::doubleFractionLength(increment, &singleDigit);
345     PrecisionUnion union_;
346     union_.increment = settings;
347     if (singleDigit == 1) {
348         // NOTE: In C++, we must return the correct value type with the correct union.
349         // It would be invalid to return a RND_FRACTION here because the methods on the
350         // IncrementPrecision type assume that the union is backed by increment data.
351         return {RND_INCREMENT_ONE, union_};
352     } else if (singleDigit == 5) {
353         return {RND_INCREMENT_FIVE, union_};
354     } else {
355         return {RND_INCREMENT, union_};
356     }
357 }
358 
constructCurrency(UCurrencyUsage usage)359 CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
360     PrecisionUnion union_;
361     union_.currencyUsage = usage;
362     return {RND_CURRENCY, union_};
363 }
364 
365 
RoundingImpl(const Precision & precision,UNumberFormatRoundingMode roundingMode,const CurrencyUnit & currency,UErrorCode & status)366 RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
367                            const CurrencyUnit& currency, UErrorCode& status)
368         : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
369     if (precision.fType == Precision::RND_CURRENCY) {
370         fPrecision = precision.withCurrency(currency, status);
371     }
372 }
373 
passThrough()374 RoundingImpl RoundingImpl::passThrough() {
375     return {};
376 }
377 
isSignificantDigits() const378 bool RoundingImpl::isSignificantDigits() const {
379     return fPrecision.fType == Precision::RND_SIGNIFICANT;
380 }
381 
382 int32_t
chooseMultiplierAndApply(impl::DecimalQuantity & input,const impl::MultiplierProducer & producer,UErrorCode & status)383 RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
384                                   UErrorCode &status) {
385     // Do not call this method with zero, NaN, or infinity.
386     U_ASSERT(!input.isZeroish());
387 
388     // Perform the first attempt at rounding.
389     int magnitude = input.getMagnitude();
390     int multiplier = producer.getMultiplier(magnitude);
391     input.adjustMagnitude(multiplier);
392     apply(input, status);
393 
394     // If the number rounded to zero, exit.
395     if (input.isZeroish() || U_FAILURE(status)) {
396         return multiplier;
397     }
398 
399     // If the new magnitude after rounding is the same as it was before rounding, then we are done.
400     // This case applies to most numbers.
401     if (input.getMagnitude() == magnitude + multiplier) {
402         return multiplier;
403     }
404 
405     // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
406     // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
407     // we do not need to make any more adjustments.
408     int _multiplier = producer.getMultiplier(magnitude + 1);
409     if (multiplier == _multiplier) {
410         return multiplier;
411     }
412 
413     // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
414     // Fix the magnitude and re-apply the rounding strategy.
415     input.adjustMagnitude(_multiplier - multiplier);
416     apply(input, status);
417     return _multiplier;
418 }
419 
420 /** This is the method that contains the actual rounding logic. */
apply(impl::DecimalQuantity & value,UErrorCode & status) const421 void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
422     if (U_FAILURE(status)) {
423         return;
424     }
425     if (fPassThrough) {
426         return;
427     }
428     int32_t resolvedMinFraction = 0;
429     switch (fPrecision.fType) {
430         case Precision::RND_BOGUS:
431         case Precision::RND_ERROR:
432             // Errors should be caught before the apply() method is called
433             status = U_INTERNAL_PROGRAM_ERROR;
434             break;
435 
436         case Precision::RND_NONE:
437             value.roundToInfinity();
438             break;
439 
440         case Precision::RND_FRACTION:
441             value.roundToMagnitude(
442                     getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
443                     fRoundingMode,
444                     status);
445             resolvedMinFraction =
446                     uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac));
447             break;
448 
449         case Precision::RND_SIGNIFICANT:
450             value.roundToMagnitude(
451                     getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
452                     fRoundingMode,
453                     status);
454             resolvedMinFraction =
455                     uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig));
456             // Make sure that digits are displayed on zero.
457             if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
458                 value.setMinInteger(1);
459             }
460             break;
461 
462         case Precision::RND_FRACTION_SIGNIFICANT: {
463             int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
464             int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig);
465             int32_t roundingMag;
466             if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
467                 roundingMag = uprv_min(roundingMag1, roundingMag2);
468             } else {
469                 roundingMag = uprv_max(roundingMag1, roundingMag2);
470             }
471             value.roundToMagnitude(roundingMag, fRoundingMode, status);
472 
473             int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
474             int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig);
475             int32_t displayMag = uprv_min(displayMag1, displayMag2);
476             resolvedMinFraction = uprv_max(0, -displayMag);
477 
478             break;
479         }
480 
481         case Precision::RND_INCREMENT:
482             value.roundToIncrement(
483                     fPrecision.fUnion.increment.fIncrement,
484                     fRoundingMode,
485                     status);
486             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
487             break;
488 
489         case Precision::RND_INCREMENT_ONE:
490             value.roundToMagnitude(
491                     -fPrecision.fUnion.increment.fMaxFrac,
492                     fRoundingMode,
493                     status);
494             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
495             break;
496 
497         case Precision::RND_INCREMENT_FIVE:
498             value.roundToNickel(
499                     -fPrecision.fUnion.increment.fMaxFrac,
500                     fRoundingMode,
501                     status);
502             resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
503             break;
504 
505         case Precision::RND_CURRENCY:
506             // Call .withCurrency() before .apply()!
507             UPRV_UNREACHABLE;
508 
509         default:
510             UPRV_UNREACHABLE;
511     }
512 
513     if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO ||
514             // PLURAL_OPERAND_T returns fraction digits as an integer
515             value.getPluralOperand(PLURAL_OPERAND_T) != 0) {
516         value.setMinFraction(resolvedMinFraction);
517     }
518 }
519 
apply(impl::DecimalQuantity & value,int32_t minInt,UErrorCode)520 void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
521     // This method is intended for the one specific purpose of helping print "00.000E0".
522     // Question: Is it useful to look at trailingZeroDisplay here?
523     U_ASSERT(isSignificantDigits());
524     U_ASSERT(value.isZeroish());
525     value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
526 }
527 
528 #endif /* #if !UCONFIG_NO_FORMATTING */
529