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