• 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT
7 
8 #include "uassert.h"
9 #include "unicode/numberformatter.h"
10 #include "number_types.h"
11 #include "number_decimalquantity.h"
12 
13 using namespace icu;
14 using namespace icu::number;
15 using namespace icu::number::impl;
16 
17 namespace {
18 
getRoundingMagnitudeFraction(int maxFrac)19 int32_t getRoundingMagnitudeFraction(int maxFrac) {
20     if (maxFrac == -1) {
21         return INT32_MIN;
22     }
23     return -maxFrac;
24 }
25 
getRoundingMagnitudeSignificant(const DecimalQuantity & value,int maxSig)26 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) {
27     if (maxSig == -1) {
28         return INT32_MIN;
29     }
30     int magnitude = value.isZero() ? 0 : value.getMagnitude();
31     return magnitude - maxSig + 1;
32 }
33 
getDisplayMagnitudeFraction(int minFrac)34 int32_t getDisplayMagnitudeFraction(int minFrac) {
35     if (minFrac == 0) {
36         return INT32_MAX;
37     }
38     return -minFrac;
39 }
40 
getDisplayMagnitudeSignificant(const DecimalQuantity & value,int minSig)41 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
42     int magnitude = value.isZero() ? 0 : value.getMagnitude();
43     return magnitude - minSig + 1;
44 }
45 
46 }
47 
48 
unlimited()49 Rounder Rounder::unlimited() {
50     return Rounder(RND_NONE, {}, kDefaultMode);
51 }
52 
integer()53 FractionRounder Rounder::integer() {
54     return constructFraction(0, 0);
55 }
56 
fixedFraction(int32_t minMaxFractionPlaces)57 FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) {
58     if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
59         return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
60     } else {
61         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
62     }
63 }
64 
minFraction(int32_t minFractionPlaces)65 FractionRounder Rounder::minFraction(int32_t minFractionPlaces) {
66     if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
67         return constructFraction(minFractionPlaces, -1);
68     } else {
69         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
70     }
71 }
72 
maxFraction(int32_t maxFractionPlaces)73 FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) {
74     if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
75         return constructFraction(0, maxFractionPlaces);
76     } else {
77         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
78     }
79 }
80 
minMaxFraction(int32_t minFractionPlaces,int32_t maxFractionPlaces)81 FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
82     if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
83         minFractionPlaces <= maxFractionPlaces) {
84         return constructFraction(minFractionPlaces, maxFractionPlaces);
85     } else {
86         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
87     }
88 }
89 
fixedDigits(int32_t minMaxSignificantDigits)90 Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) {
91     if (minMaxSignificantDigits >= 0 && minMaxSignificantDigits <= kMaxIntFracSig) {
92         return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
93     } else {
94         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
95     }
96 }
97 
minDigits(int32_t minSignificantDigits)98 Rounder Rounder::minDigits(int32_t minSignificantDigits) {
99     if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) {
100         return constructSignificant(minSignificantDigits, -1);
101     } else {
102         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
103     }
104 }
105 
maxDigits(int32_t maxSignificantDigits)106 Rounder Rounder::maxDigits(int32_t maxSignificantDigits) {
107     if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) {
108         return constructSignificant(0, maxSignificantDigits);
109     } else {
110         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
111     }
112 }
113 
minMaxDigits(int32_t minSignificantDigits,int32_t maxSignificantDigits)114 Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
115     if (minSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig &&
116         minSignificantDigits <= maxSignificantDigits) {
117         return constructSignificant(minSignificantDigits, maxSignificantDigits);
118     } else {
119         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
120     }
121 }
122 
increment(double roundingIncrement)123 IncrementRounder Rounder::increment(double roundingIncrement) {
124     if (roundingIncrement > 0.0) {
125         return constructIncrement(roundingIncrement, 0);
126     } else {
127         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
128     }
129 }
130 
currency(UCurrencyUsage currencyUsage)131 CurrencyRounder Rounder::currency(UCurrencyUsage currencyUsage) {
132     return constructCurrency(currencyUsage);
133 }
134 
withMode(RoundingMode roundingMode) const135 Rounder Rounder::withMode(RoundingMode roundingMode) const {
136     if (fType == RND_ERROR) { return *this; } // no-op in error state
137     return {fType, fUnion, roundingMode};
138 }
139 
withMinDigits(int32_t minSignificantDigits) const140 Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const {
141     if (fType == RND_ERROR) { return *this; } // no-op in error state
142     if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) {
143         return constructFractionSignificant(*this, minSignificantDigits, -1);
144     } else {
145         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
146     }
147 }
148 
withMaxDigits(int32_t maxSignificantDigits) const149 Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const {
150     if (fType == RND_ERROR) { return *this; } // no-op in error state
151     if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) {
152         return constructFractionSignificant(*this, -1, maxSignificantDigits);
153     } else {
154         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
155     }
156 }
157 
158 // Private method on base class
withCurrency(const CurrencyUnit & currency,UErrorCode & status) const159 Rounder Rounder::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
160     if (fType == RND_ERROR) { return *this; } // no-op in error state
161     U_ASSERT(fType == RND_CURRENCY);
162     const char16_t *isoCode = currency.getISOCurrency();
163     double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
164     int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
165             isoCode, fUnion.currencyUsage, &status);
166     if (increment != 0.0) {
167         return constructIncrement(increment, minMaxFrac);
168     } else {
169         return constructFraction(minMaxFrac, minMaxFrac);
170     }
171 }
172 
173 // Public method on CurrencyRounder subclass
withCurrency(const CurrencyUnit & currency) const174 Rounder CurrencyRounder::withCurrency(const CurrencyUnit &currency) const {
175     UErrorCode localStatus = U_ZERO_ERROR;
176     Rounder result = Rounder::withCurrency(currency, localStatus);
177     if (U_FAILURE(localStatus)) {
178         return {localStatus};
179     }
180     return result;
181 }
182 
withMinFraction(int32_t minFrac) const183 Rounder IncrementRounder::withMinFraction(int32_t minFrac) const {
184     if (fType == RND_ERROR) { return *this; } // no-op in error state
185     if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
186         return constructIncrement(fUnion.increment.fIncrement, minFrac);
187     } else {
188         return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR};
189     }
190 }
191 
constructFraction(int32_t minFrac,int32_t maxFrac)192 FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) {
193     FractionSignificantSettings settings;
194     settings.fMinFrac = static_cast<int8_t> (minFrac);
195     settings.fMaxFrac = static_cast<int8_t> (maxFrac);
196     settings.fMinSig = -1;
197     settings.fMaxSig = -1;
198     RounderUnion union_;
199     union_.fracSig = settings;
200     return {RND_FRACTION, union_, kDefaultMode};
201 }
202 
constructSignificant(int32_t minSig,int32_t maxSig)203 Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) {
204     FractionSignificantSettings settings;
205     settings.fMinFrac = -1;
206     settings.fMaxFrac = -1;
207     settings.fMinSig = static_cast<int8_t>(minSig);
208     settings.fMaxSig = static_cast<int8_t>(maxSig);
209     RounderUnion union_;
210     union_.fracSig = settings;
211     return {RND_SIGNIFICANT, union_, kDefaultMode};
212 }
213 
214 Rounder
constructFractionSignificant(const FractionRounder & base,int32_t minSig,int32_t maxSig)215 Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) {
216     FractionSignificantSettings settings = base.fUnion.fracSig;
217     settings.fMinSig = static_cast<int8_t>(minSig);
218     settings.fMaxSig = static_cast<int8_t>(maxSig);
219     RounderUnion union_;
220     union_.fracSig = settings;
221     return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
222 }
223 
constructIncrement(double increment,int32_t minFrac)224 IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) {
225     IncrementSettings settings;
226     settings.fIncrement = increment;
227     settings.fMinFrac = minFrac;
228     RounderUnion union_;
229     union_.increment = settings;
230     return {RND_INCREMENT, union_, kDefaultMode};
231 }
232 
constructCurrency(UCurrencyUsage usage)233 CurrencyRounder Rounder::constructCurrency(UCurrencyUsage usage) {
234     RounderUnion union_;
235     union_.currencyUsage = usage;
236     return {RND_CURRENCY, union_, kDefaultMode};
237 }
238 
constructPassThrough()239 Rounder Rounder::constructPassThrough() {
240     RounderUnion union_;
241     union_.errorCode = U_ZERO_ERROR; // initialize the variable
242     return {RND_PASS_THROUGH, union_, kDefaultMode};
243 }
244 
setLocaleData(const CurrencyUnit & currency,UErrorCode & status)245 void Rounder::setLocaleData(const CurrencyUnit &currency, UErrorCode &status) {
246     if (fType == RND_CURRENCY) {
247         *this = withCurrency(currency, status);
248     }
249 }
250 
251 int32_t
chooseMultiplierAndApply(impl::DecimalQuantity & input,const impl::MultiplierProducer & producer,UErrorCode & status)252 Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
253                                   UErrorCode &status) {
254     // TODO: Make a better and more efficient implementation.
255     // TODO: Avoid the object creation here.
256     DecimalQuantity copy(input);
257 
258     U_ASSERT(!input.isZero());
259     int32_t magnitude = input.getMagnitude();
260     int32_t multiplier = producer.getMultiplier(magnitude);
261     input.adjustMagnitude(multiplier);
262     apply(input, status);
263 
264     // If the number turned to zero when rounding, do not re-attempt the rounding.
265     if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) {
266         magnitude += 1;
267         input = copy;
268         multiplier = producer.getMultiplier(magnitude);
269         input.adjustMagnitude(multiplier);
270         U_ASSERT(input.getMagnitude() == magnitude + multiplier - 1);
271         apply(input, status);
272         U_ASSERT(input.getMagnitude() == magnitude + multiplier);
273     }
274 
275     return multiplier;
276 }
277 
278 /** This is the method that contains the actual rounding logic. */
apply(impl::DecimalQuantity & value,UErrorCode & status) const279 void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
280     switch (fType) {
281         case RND_BOGUS:
282         case RND_ERROR:
283             // Errors should be caught before the apply() method is called
284             status = U_INTERNAL_PROGRAM_ERROR;
285             break;
286 
287         case RND_NONE:
288             value.roundToInfinity();
289             break;
290 
291         case RND_FRACTION:
292             value.roundToMagnitude(
293                     getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac), fRoundingMode, status);
294             value.setFractionLength(
295                     uprv_max(0, -getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac)), INT32_MAX);
296             break;
297 
298         case RND_SIGNIFICANT:
299             value.roundToMagnitude(
300                     getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig),
301                     fRoundingMode,
302                     status);
303             value.setFractionLength(
304                     uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)),
305                     INT32_MAX);
306             break;
307 
308         case RND_FRACTION_SIGNIFICANT: {
309             int32_t displayMag = getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac);
310             int32_t roundingMag = getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac);
311             if (fUnion.fracSig.fMinSig == -1) {
312                 // Max Sig override
313                 int32_t candidate = getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig);
314                 roundingMag = uprv_max(roundingMag, candidate);
315             } else {
316                 // Min Sig override
317                 int32_t candidate = getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig);
318                 roundingMag = uprv_min(roundingMag, candidate);
319             }
320             value.roundToMagnitude(roundingMag, fRoundingMode, status);
321             value.setFractionLength(uprv_max(0, -displayMag), INT32_MAX);
322             break;
323         }
324 
325         case RND_INCREMENT:
326             value.roundToIncrement(
327                 fUnion.increment.fIncrement, fRoundingMode, fUnion.increment.fMinFrac, status);
328             value.setFractionLength(fUnion.increment.fMinFrac, fUnion.increment.fMinFrac);
329             break;
330 
331         case RND_CURRENCY:
332             // Call .withCurrency() before .apply()!
333             U_ASSERT(false);
334 
335         case RND_PASS_THROUGH:
336             break;
337     }
338 }
339 
apply(impl::DecimalQuantity & value,int32_t minInt,UErrorCode)340 void Rounder::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
341     // This method is intended for the one specific purpose of helping print "00.000E0".
342     U_ASSERT(fType == RND_SIGNIFICANT);
343     U_ASSERT(value.isZero());
344     value.setFractionLength(fUnion.fracSig.fMinSig - minInt, INT32_MAX);
345 }
346 
347 #endif /* #if !UCONFIG_NO_FORMATTING */
348