• 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 "uassert.h"
9 #include "unicode/numberformatter.h"
10 #include "number_types.h"
11 #include "number_decimalquantity.h"
12 #include "double-conversion.h"
13 #include "number_roundingutils.h"
14 #include "putilimp.h"
15 
16 using namespace icu;
17 using namespace icu::number;
18 using namespace icu::number::impl;
19 
20 
21 using double_conversion::DoubleToStringConverter;
22 
23 namespace {
24 
getRoundingMagnitudeFraction(int maxFrac)25 int32_t getRoundingMagnitudeFraction(int maxFrac) {
26     if (maxFrac == -1) {
27         return INT32_MIN;
28     }
29     return -maxFrac;
30 }
31 
getRoundingMagnitudeSignificant(const DecimalQuantity & value,int maxSig)32 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) {
33     if (maxSig == -1) {
34         return INT32_MIN;
35     }
36     int magnitude = value.isZero() ? 0 : value.getMagnitude();
37     return magnitude - maxSig + 1;
38 }
39 
getDisplayMagnitudeFraction(int minFrac)40 int32_t getDisplayMagnitudeFraction(int minFrac) {
41     if (minFrac == 0) {
42         return INT32_MAX;
43     }
44     return -minFrac;
45 }
46 
getDisplayMagnitudeSignificant(const DecimalQuantity & value,int minSig)47 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
48     int magnitude = value.isZero() ? 0 : value.getMagnitude();
49     return magnitude - minSig + 1;
50 }
51 
52 }
53 
54 
55 MultiplierProducer::~MultiplierProducer() = default;
56 
57 
doubleFractionLength(double input)58 digits_t roundingutils::doubleFractionLength(double input) {
59     char buffer[DoubleToStringConverter::kBase10MaximalLength + 1];
60     bool sign; // unused; always positive
61     int32_t length;
62     int32_t point;
63     DoubleToStringConverter::DoubleToAscii(
64             input,
65             DoubleToStringConverter::DtoaMode::SHORTEST,
66             0,
67             buffer,
68             sizeof(buffer),
69             &sign,
70             &length,
71             &point
72     );
73 
74     return static_cast<digits_t>(length - point);
75 }
76 
77 
unlimited()78 Precision Precision::unlimited() {
79     return Precision(RND_NONE, {}, kDefaultMode);
80 }
81 
integer()82 FractionPrecision Precision::integer() {
83     return constructFraction(0, 0);
84 }
85 
fixedFraction(int32_t minMaxFractionPlaces)86 FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
87     if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
88         return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
89     } else {
90         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
91     }
92 }
93 
minFraction(int32_t minFractionPlaces)94 FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
95     if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
96         return constructFraction(minFractionPlaces, -1);
97     } else {
98         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
99     }
100 }
101 
maxFraction(int32_t maxFractionPlaces)102 FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
103     if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
104         return constructFraction(0, maxFractionPlaces);
105     } else {
106         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
107     }
108 }
109 
minMaxFraction(int32_t minFractionPlaces,int32_t maxFractionPlaces)110 FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
111     if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
112         minFractionPlaces <= maxFractionPlaces) {
113         return constructFraction(minFractionPlaces, maxFractionPlaces);
114     } else {
115         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
116     }
117 }
118 
fixedSignificantDigits(int32_t minMaxSignificantDigits)119 Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
120     if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
121         return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
122     } else {
123         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
124     }
125 }
126 
minSignificantDigits(int32_t minSignificantDigits)127 Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
128     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
129         return constructSignificant(minSignificantDigits, -1);
130     } else {
131         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
132     }
133 }
134 
maxSignificantDigits(int32_t maxSignificantDigits)135 Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
136     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
137         return constructSignificant(1, maxSignificantDigits);
138     } else {
139         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
140     }
141 }
142 
minMaxSignificantDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits)143 Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
144     if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
145         minSignificantDigits <= maxSignificantDigits) {
146         return constructSignificant(minSignificantDigits, maxSignificantDigits);
147     } else {
148         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
149     }
150 }
151 
increment(double roundingIncrement)152 IncrementPrecision Precision::increment(double roundingIncrement) {
153     if (roundingIncrement > 0.0) {
154         return constructIncrement(roundingIncrement, 0);
155     } else {
156         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
157     }
158 }
159 
currency(UCurrencyUsage currencyUsage)160 CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
161     return constructCurrency(currencyUsage);
162 }
163 
withMode(RoundingMode roundingMode) const164 Precision Precision::withMode(RoundingMode roundingMode) const {
165     if (fType == RND_ERROR) { return *this; } // no-op in error state
166     Precision retval = *this;
167     retval.fRoundingMode = roundingMode;
168     return retval;
169 }
170 
withMinDigits(int32_t minSignificantDigits) const171 Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
172     if (fType == RND_ERROR) { return *this; } // no-op in error state
173     if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
174         return constructFractionSignificant(*this, minSignificantDigits, -1);
175     } else {
176         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
177     }
178 }
179 
withMaxDigits(int32_t maxSignificantDigits) const180 Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
181     if (fType == RND_ERROR) { return *this; } // no-op in error state
182     if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
183         return constructFractionSignificant(*this, -1, maxSignificantDigits);
184     } else {
185         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
186     }
187 }
188 
189 // Private method on base class
withCurrency(const CurrencyUnit & currency,UErrorCode & status) const190 Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
191     if (fType == RND_ERROR) { return *this; } // no-op in error state
192     U_ASSERT(fType == RND_CURRENCY);
193     const char16_t *isoCode = currency.getISOCurrency();
194     double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
195     int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
196             isoCode, fUnion.currencyUsage, &status);
197     if (increment != 0.0) {
198         return constructIncrement(increment, minMaxFrac);
199     } else {
200         return constructFraction(minMaxFrac, minMaxFrac);
201     }
202 }
203 
204 // Public method on CurrencyPrecision subclass
withCurrency(const CurrencyUnit & currency) const205 Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
206     UErrorCode localStatus = U_ZERO_ERROR;
207     Precision result = Precision::withCurrency(currency, localStatus);
208     if (U_FAILURE(localStatus)) {
209         return {localStatus};
210     }
211     return result;
212 }
213 
withMinFraction(int32_t minFrac) const214 Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
215     if (fType == RND_ERROR) { return *this; } // no-op in error state
216     if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
217         return constructIncrement(fUnion.increment.fIncrement, minFrac);
218     } else {
219         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
220     }
221 }
222 
constructFraction(int32_t minFrac,int32_t maxFrac)223 FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
224     FractionSignificantSettings settings;
225     settings.fMinFrac = static_cast<digits_t>(minFrac);
226     settings.fMaxFrac = static_cast<digits_t>(maxFrac);
227     settings.fMinSig = -1;
228     settings.fMaxSig = -1;
229     PrecisionUnion union_;
230     union_.fracSig = settings;
231     return {RND_FRACTION, union_, kDefaultMode};
232 }
233 
constructSignificant(int32_t minSig,int32_t maxSig)234 Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
235     FractionSignificantSettings settings;
236     settings.fMinFrac = -1;
237     settings.fMaxFrac = -1;
238     settings.fMinSig = static_cast<digits_t>(minSig);
239     settings.fMaxSig = static_cast<digits_t>(maxSig);
240     PrecisionUnion union_;
241     union_.fracSig = settings;
242     return {RND_SIGNIFICANT, union_, kDefaultMode};
243 }
244 
245 Precision
constructFractionSignificant(const FractionPrecision & base,int32_t minSig,int32_t maxSig)246 Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) {
247     FractionSignificantSettings settings = base.fUnion.fracSig;
248     settings.fMinSig = static_cast<digits_t>(minSig);
249     settings.fMaxSig = static_cast<digits_t>(maxSig);
250     PrecisionUnion union_;
251     union_.fracSig = settings;
252     return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
253 }
254 
constructIncrement(double increment,int32_t minFrac)255 IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
256     IncrementSettings settings;
257     settings.fIncrement = increment;
258     settings.fMinFrac = static_cast<digits_t>(minFrac);
259     // One of the few pre-computed quantities:
260     // Note: it is possible for minFrac to be more than maxFrac... (misleading)
261     settings.fMaxFrac = roundingutils::doubleFractionLength(increment);
262     PrecisionUnion union_;
263     union_.increment = settings;
264     return {RND_INCREMENT, union_, kDefaultMode};
265 }
266 
constructCurrency(UCurrencyUsage usage)267 CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
268     PrecisionUnion union_;
269     union_.currencyUsage = usage;
270     return {RND_CURRENCY, union_, kDefaultMode};
271 }
272 
273 
RoundingImpl(const Precision & precision,UNumberFormatRoundingMode roundingMode,const CurrencyUnit & currency,UErrorCode & status)274 RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
275                            const CurrencyUnit& currency, UErrorCode& status)
276         : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
277     if (precision.fType == Precision::RND_CURRENCY) {
278         fPrecision = precision.withCurrency(currency, status);
279     }
280 }
281 
passThrough()282 RoundingImpl RoundingImpl::passThrough() {
283     RoundingImpl retval;
284     retval.fPassThrough = true;
285     return retval;
286 }
287 
isSignificantDigits() const288 bool RoundingImpl::isSignificantDigits() const {
289     return fPrecision.fType == Precision::RND_SIGNIFICANT;
290 }
291 
292 int32_t
chooseMultiplierAndApply(impl::DecimalQuantity & input,const impl::MultiplierProducer & producer,UErrorCode & status)293 RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
294                                   UErrorCode &status) {
295     // Do not call this method with zero.
296     U_ASSERT(!input.isZero());
297 
298     // Perform the first attempt at rounding.
299     int magnitude = input.getMagnitude();
300     int multiplier = producer.getMultiplier(magnitude);
301     input.adjustMagnitude(multiplier);
302     apply(input, status);
303 
304     // If the number rounded to zero, exit.
305     if (input.isZero() || U_FAILURE(status)) {
306         return multiplier;
307     }
308 
309     // If the new magnitude after rounding is the same as it was before rounding, then we are done.
310     // This case applies to most numbers.
311     if (input.getMagnitude() == magnitude + multiplier) {
312         return multiplier;
313     }
314 
315     // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
316     // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
317     // we do not need to make any more adjustments.
318     int _multiplier = producer.getMultiplier(magnitude + 1);
319     if (multiplier == _multiplier) {
320         return multiplier;
321     }
322 
323     // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
324     // Fix the magnitude and re-apply the rounding strategy.
325     input.adjustMagnitude(_multiplier - multiplier);
326     apply(input, status);
327     return _multiplier;
328 }
329 
330 /** This is the method that contains the actual rounding logic. */
apply(impl::DecimalQuantity & value,UErrorCode & status) const331 void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
332     if (fPassThrough) {
333         return;
334     }
335     switch (fPrecision.fType) {
336         case Precision::RND_BOGUS:
337         case Precision::RND_ERROR:
338             // Errors should be caught before the apply() method is called
339             status = U_INTERNAL_PROGRAM_ERROR;
340             break;
341 
342         case Precision::RND_NONE:
343             value.roundToInfinity();
344             break;
345 
346         case Precision::RND_FRACTION:
347             value.roundToMagnitude(
348                     getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
349                     fRoundingMode,
350                     status);
351             value.setFractionLength(
352                     uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)),
353                     INT32_MAX);
354             break;
355 
356         case Precision::RND_SIGNIFICANT:
357             value.roundToMagnitude(
358                     getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
359                     fRoundingMode,
360                     status);
361             value.setFractionLength(
362                     uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)),
363                     INT32_MAX);
364             // Make sure that digits are displayed on zero.
365             if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) {
366                 value.setIntegerLength(1, INT32_MAX);
367             }
368             break;
369 
370         case Precision::RND_FRACTION_SIGNIFICANT: {
371             int32_t displayMag = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
372             int32_t roundingMag = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
373             if (fPrecision.fUnion.fracSig.fMinSig == -1) {
374                 // Max Sig override
375                 int32_t candidate = getRoundingMagnitudeSignificant(
376                         value,
377                         fPrecision.fUnion.fracSig.fMaxSig);
378                 roundingMag = uprv_max(roundingMag, candidate);
379             } else {
380                 // Min Sig override
381                 int32_t candidate = getDisplayMagnitudeSignificant(
382                         value,
383                         fPrecision.fUnion.fracSig.fMinSig);
384                 roundingMag = uprv_min(roundingMag, candidate);
385             }
386             value.roundToMagnitude(roundingMag, fRoundingMode, status);
387             value.setFractionLength(uprv_max(0, -displayMag), INT32_MAX);
388             break;
389         }
390 
391         case Precision::RND_INCREMENT:
392             value.roundToIncrement(
393                     fPrecision.fUnion.increment.fIncrement,
394                     fRoundingMode,
395                     fPrecision.fUnion.increment.fMaxFrac,
396                     status);
397             value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX);
398             break;
399 
400         case Precision::RND_CURRENCY:
401             // Call .withCurrency() before .apply()!
402             U_ASSERT(false);
403             break;
404     }
405 }
406 
apply(impl::DecimalQuantity & value,int32_t minInt,UErrorCode)407 void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
408     // This method is intended for the one specific purpose of helping print "00.000E0".
409     U_ASSERT(isSignificantDigits());
410     U_ASSERT(value.isZero());
411     value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX);
412 }
413 
414 #endif /* #if !UCONFIG_NO_FORMATTING */
415