• Home
  • History
  • Annotate
  • 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 "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  
increment(double roundingIncrement)196  IncrementPrecision Precision::increment(double roundingIncrement) {
197      if (roundingIncrement > 0.0) {
198          return constructIncrement(roundingIncrement, 0);
199      } else {
200          return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
201      }
202  }
203  
currency(UCurrencyUsage currencyUsage)204  CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
205      return constructCurrency(currencyUsage);
206  }
207  
withMinDigits(int32_t minSignificantDigits) const208  Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
209      if (fType == RND_ERROR) { return *this; } // no-op in error state
210      if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
211          return constructFractionSignificant(*this, minSignificantDigits, -1);
212      } else {
213          return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
214      }
215  }
216  
withMaxDigits(int32_t maxSignificantDigits) const217  Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
218      if (fType == RND_ERROR) { return *this; } // no-op in error state
219      if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
220          return constructFractionSignificant(*this, -1, maxSignificantDigits);
221      } else {
222          return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
223      }
224  }
225  
226  // Private method on base class
withCurrency(const CurrencyUnit & currency,UErrorCode & status) const227  Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
228      if (fType == RND_ERROR) { return *this; } // no-op in error state
229      U_ASSERT(fType == RND_CURRENCY);
230      const char16_t *isoCode = currency.getISOCurrency();
231      double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
232      int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
233              isoCode, fUnion.currencyUsage, &status);
234      if (increment != 0.0) {
235          return constructIncrement(increment, minMaxFrac);
236      } else {
237          return constructFraction(minMaxFrac, minMaxFrac);
238      }
239  }
240  
241  // Public method on CurrencyPrecision subclass
withCurrency(const CurrencyUnit & currency) const242  Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
243      UErrorCode localStatus = U_ZERO_ERROR;
244      Precision result = Precision::withCurrency(currency, localStatus);
245      if (U_FAILURE(localStatus)) {
246          return {localStatus};
247      }
248      return result;
249  }
250  
withMinFraction(int32_t minFrac) const251  Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
252      if (fType == RND_ERROR) { return *this; } // no-op in error state
253      if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
254          return constructIncrement(fUnion.increment.fIncrement, minFrac);
255      } else {
256          return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
257      }
258  }
259  
constructFraction(int32_t minFrac,int32_t maxFrac)260  FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
261      FractionSignificantSettings settings;
262      settings.fMinFrac = static_cast<digits_t>(minFrac);
263      settings.fMaxFrac = static_cast<digits_t>(maxFrac);
264      settings.fMinSig = -1;
265      settings.fMaxSig = -1;
266      PrecisionUnion union_;
267      union_.fracSig = settings;
268      return {RND_FRACTION, union_};
269  }
270  
constructSignificant(int32_t minSig,int32_t maxSig)271  Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
272      FractionSignificantSettings settings;
273      settings.fMinFrac = -1;
274      settings.fMaxFrac = -1;
275      settings.fMinSig = static_cast<digits_t>(minSig);
276      settings.fMaxSig = static_cast<digits_t>(maxSig);
277      PrecisionUnion union_;
278      union_.fracSig = settings;
279      return {RND_SIGNIFICANT, union_};
280  }
281  
282  Precision
constructFractionSignificant(const FractionPrecision & base,int32_t minSig,int32_t maxSig)283  Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) {
284      FractionSignificantSettings settings = base.fUnion.fracSig;
285      settings.fMinSig = static_cast<digits_t>(minSig);
286      settings.fMaxSig = static_cast<digits_t>(maxSig);
287      PrecisionUnion union_;
288      union_.fracSig = settings;
289      return {RND_FRACTION_SIGNIFICANT, union_};
290  }
291  
constructIncrement(double increment,int32_t minFrac)292  IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
293      IncrementSettings settings;
294      // Note: For number formatting, fIncrement is used for RND_INCREMENT but not
295      // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all
296      // three when constructing a skeleton.
297      settings.fIncrement = increment;
298      settings.fMinFrac = static_cast<digits_t>(minFrac);
299      // One of the few pre-computed quantities:
300      // Note: it is possible for minFrac to be more than maxFrac... (misleading)
301      int8_t singleDigit;
302      settings.fMaxFrac = roundingutils::doubleFractionLength(increment, &singleDigit);
303      PrecisionUnion union_;
304      union_.increment = settings;
305      if (singleDigit == 1) {
306          // NOTE: In C++, we must return the correct value type with the correct union.
307          // It would be invalid to return a RND_FRACTION here because the methods on the
308          // IncrementPrecision type assume that the union is backed by increment data.
309          return {RND_INCREMENT_ONE, union_};
310      } else if (singleDigit == 5) {
311          return {RND_INCREMENT_FIVE, union_};
312      } else {
313          return {RND_INCREMENT, union_};
314      }
315  }
316  
constructCurrency(UCurrencyUsage usage)317  CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
318      PrecisionUnion union_;
319      union_.currencyUsage = usage;
320      return {RND_CURRENCY, union_};
321  }
322  
323  
RoundingImpl(const Precision & precision,UNumberFormatRoundingMode roundingMode,const CurrencyUnit & currency,UErrorCode & status)324  RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
325                             const CurrencyUnit& currency, UErrorCode& status)
326          : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
327      if (precision.fType == Precision::RND_CURRENCY) {
328          fPrecision = precision.withCurrency(currency, status);
329      }
330  }
331  
passThrough()332  RoundingImpl RoundingImpl::passThrough() {
333      return {};
334  }
335  
isSignificantDigits() const336  bool RoundingImpl::isSignificantDigits() const {
337      return fPrecision.fType == Precision::RND_SIGNIFICANT;
338  }
339  
340  int32_t
chooseMultiplierAndApply(impl::DecimalQuantity & input,const impl::MultiplierProducer & producer,UErrorCode & status)341  RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
342                                    UErrorCode &status) {
343      // Do not call this method with zero, NaN, or infinity.
344      U_ASSERT(!input.isZeroish());
345  
346      // Perform the first attempt at rounding.
347      int magnitude = input.getMagnitude();
348      int multiplier = producer.getMultiplier(magnitude);
349      input.adjustMagnitude(multiplier);
350      apply(input, status);
351  
352      // If the number rounded to zero, exit.
353      if (input.isZeroish() || U_FAILURE(status)) {
354          return multiplier;
355      }
356  
357      // If the new magnitude after rounding is the same as it was before rounding, then we are done.
358      // This case applies to most numbers.
359      if (input.getMagnitude() == magnitude + multiplier) {
360          return multiplier;
361      }
362  
363      // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
364      // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
365      // we do not need to make any more adjustments.
366      int _multiplier = producer.getMultiplier(magnitude + 1);
367      if (multiplier == _multiplier) {
368          return multiplier;
369      }
370  
371      // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
372      // Fix the magnitude and re-apply the rounding strategy.
373      input.adjustMagnitude(_multiplier - multiplier);
374      apply(input, status);
375      return _multiplier;
376  }
377  
378  /** This is the method that contains the actual rounding logic. */
apply(impl::DecimalQuantity & value,UErrorCode & status) const379  void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
380      if (U_FAILURE(status)) {
381          return;
382      }
383      if (fPassThrough) {
384          return;
385      }
386      switch (fPrecision.fType) {
387          case Precision::RND_BOGUS:
388          case Precision::RND_ERROR:
389              // Errors should be caught before the apply() method is called
390              status = U_INTERNAL_PROGRAM_ERROR;
391              break;
392  
393          case Precision::RND_NONE:
394              value.roundToInfinity();
395              break;
396  
397          case Precision::RND_FRACTION:
398              value.roundToMagnitude(
399                      getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
400                      fRoundingMode,
401                      status);
402              value.setMinFraction(
403                      uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)));
404              break;
405  
406          case Precision::RND_SIGNIFICANT:
407              value.roundToMagnitude(
408                      getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
409                      fRoundingMode,
410                      status);
411              value.setMinFraction(
412                      uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)));
413              // Make sure that digits are displayed on zero.
414              if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
415                  value.setMinInteger(1);
416              }
417              break;
418  
419          case Precision::RND_FRACTION_SIGNIFICANT: {
420              int32_t displayMag = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
421              int32_t roundingMag = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
422              if (fPrecision.fUnion.fracSig.fMinSig == -1) {
423                  // Max Sig override
424                  int32_t candidate = getRoundingMagnitudeSignificant(
425                          value,
426                          fPrecision.fUnion.fracSig.fMaxSig);
427                  roundingMag = uprv_max(roundingMag, candidate);
428              } else {
429                  // Min Sig override
430                  int32_t candidate = getDisplayMagnitudeSignificant(
431                          value,
432                          fPrecision.fUnion.fracSig.fMinSig);
433                  roundingMag = uprv_min(roundingMag, candidate);
434              }
435              value.roundToMagnitude(roundingMag, fRoundingMode, status);
436              value.setMinFraction(uprv_max(0, -displayMag));
437              break;
438          }
439  
440          case Precision::RND_INCREMENT:
441              value.roundToIncrement(
442                      fPrecision.fUnion.increment.fIncrement,
443                      fRoundingMode,
444                      status);
445              value.setMinFraction(fPrecision.fUnion.increment.fMinFrac);
446              break;
447  
448          case Precision::RND_INCREMENT_ONE:
449              value.roundToMagnitude(
450                      -fPrecision.fUnion.increment.fMaxFrac,
451                      fRoundingMode,
452                      status);
453              value.setMinFraction(fPrecision.fUnion.increment.fMinFrac);
454              break;
455  
456          case Precision::RND_INCREMENT_FIVE:
457              value.roundToNickel(
458                      -fPrecision.fUnion.increment.fMaxFrac,
459                      fRoundingMode,
460                      status);
461              value.setMinFraction(fPrecision.fUnion.increment.fMinFrac);
462              break;
463  
464          case Precision::RND_CURRENCY:
465              // Call .withCurrency() before .apply()!
466              UPRV_UNREACHABLE;
467  
468          default:
469              UPRV_UNREACHABLE;
470      }
471  }
472  
apply(impl::DecimalQuantity & value,int32_t minInt,UErrorCode)473  void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
474      // This method is intended for the one specific purpose of helping print "00.000E0".
475      U_ASSERT(isSignificantDigits());
476      U_ASSERT(value.isZeroish());
477      value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
478  }
479  
480  #endif /* #if !UCONFIG_NO_FORMATTING */
481