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