/src/icu/icu4c/source/i18n/number_rounding.cpp
Line | Count | Source |
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 "number_decnum.h" |
17 | | #include "putilimp.h" |
18 | | #include "string_segment.h" |
19 | | |
20 | | using namespace icu; |
21 | | using namespace icu::number; |
22 | | using namespace icu::number::impl; |
23 | | |
24 | | |
25 | | using double_conversion::DoubleToStringConverter; |
26 | | using icu::StringSegment; |
27 | | |
28 | | void number::impl::parseIncrementOption(const StringSegment &segment, |
29 | | Precision &outPrecision, |
30 | 0 | UErrorCode &status) { |
31 | | // Need to do char <-> char16_t conversion... |
32 | 0 | U_ASSERT(U_SUCCESS(status)); |
33 | 0 | CharString buffer; |
34 | 0 | SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); |
35 | | |
36 | | // Utilize DecimalQuantity/decNumber to parse this for us. |
37 | 0 | DecimalQuantity dq; |
38 | 0 | UErrorCode localStatus = U_ZERO_ERROR; |
39 | 0 | dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus); |
40 | 0 | if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) { |
41 | | // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e); |
42 | 0 | status = U_NUMBER_SKELETON_SYNTAX_ERROR; |
43 | 0 | return; |
44 | 0 | } |
45 | | // Now we break apart the number into a mantissa and exponent (magnitude). |
46 | 0 | int32_t magnitude = dq.adjustToZeroScale(); |
47 | | // setToDecNumber drops trailing zeros, so we search for the '.' manually. |
48 | 0 | for (int32_t i=0; i<buffer.length(); i++) { |
49 | 0 | if (buffer[i] == '.') { |
50 | 0 | int32_t newMagnitude = i - buffer.length() + 1; |
51 | 0 | dq.adjustMagnitude(magnitude - newMagnitude); |
52 | 0 | magnitude = newMagnitude; |
53 | 0 | break; |
54 | 0 | } |
55 | 0 | } |
56 | 0 | outPrecision = Precision::incrementExact(dq.toLong(), magnitude); |
57 | 0 | } |
58 | | |
59 | | namespace { |
60 | | |
61 | 1.68M | int32_t getRoundingMagnitudeFraction(int maxFrac) { |
62 | 1.68M | if (maxFrac == -1) { |
63 | 77 | return INT32_MIN; |
64 | 77 | } |
65 | 1.68M | return -maxFrac; |
66 | 1.68M | } |
67 | | |
68 | 4.95k | int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) { |
69 | 4.95k | if (maxSig == -1) { |
70 | 495 | return INT32_MIN; |
71 | 495 | } |
72 | 4.46k | int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); |
73 | 4.46k | return magnitude - maxSig + 1; |
74 | 4.95k | } |
75 | | |
76 | 1.68M | int32_t getDisplayMagnitudeFraction(int minFrac) { |
77 | 1.68M | if (minFrac == 0) { |
78 | 1.68M | return INT32_MAX; |
79 | 1.68M | } |
80 | 184 | return -minFrac; |
81 | 1.68M | } |
82 | | |
83 | 4.95k | int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) { |
84 | 4.95k | int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); |
85 | 4.95k | return magnitude - minSig + 1; |
86 | 4.95k | } |
87 | | |
88 | | } |
89 | | |
90 | | |
91 | 3.55k | MultiplierProducer::~MultiplierProducer() = default; |
92 | | |
93 | | |
94 | 12.6k | Precision Precision::unlimited() { |
95 | 12.6k | return Precision(RND_NONE, {}); |
96 | 12.6k | } |
97 | | |
98 | 2.86k | FractionPrecision Precision::integer() { |
99 | 2.86k | return constructFraction(0, 0); |
100 | 2.86k | } |
101 | | |
102 | 29.5k | FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) { |
103 | 29.5k | if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { |
104 | 29.5k | return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); |
105 | 29.5k | } else { |
106 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
107 | 0 | } |
108 | 29.5k | } |
109 | | |
110 | 47 | FractionPrecision Precision::minFraction(int32_t minFractionPlaces) { |
111 | 47 | if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { |
112 | 38 | return constructFraction(minFractionPlaces, -1); |
113 | 38 | } else { |
114 | 9 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
115 | 9 | } |
116 | 47 | } |
117 | | |
118 | 5.40k | FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) { |
119 | 5.40k | if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { |
120 | 5.40k | return constructFraction(0, maxFractionPlaces); |
121 | 5.40k | } else { |
122 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
123 | 0 | } |
124 | 5.40k | } |
125 | | |
126 | 300 | FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { |
127 | 300 | if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig && |
128 | 293 | minFractionPlaces <= maxFractionPlaces) { |
129 | 293 | return constructFraction(minFractionPlaces, maxFractionPlaces); |
130 | 293 | } else { |
131 | 7 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
132 | 7 | } |
133 | 300 | } |
134 | | |
135 | 0 | Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) { |
136 | 0 | if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) { |
137 | 0 | return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); |
138 | 0 | } else { |
139 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | 160 | Precision Precision::minSignificantDigits(int32_t minSignificantDigits) { |
144 | 160 | if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { |
145 | 154 | return constructSignificant(minSignificantDigits, -1); |
146 | 154 | } else { |
147 | 6 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
148 | 6 | } |
149 | 160 | } |
150 | | |
151 | 0 | Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) { |
152 | 0 | if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { |
153 | 0 | return constructSignificant(1, maxSignificantDigits); |
154 | 0 | } else { |
155 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | 81 | Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { |
160 | 81 | if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig && |
161 | 79 | minSignificantDigits <= maxSignificantDigits) { |
162 | 79 | return constructSignificant(minSignificantDigits, maxSignificantDigits); |
163 | 79 | } else { |
164 | 2 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
165 | 2 | } |
166 | 81 | } |
167 | | |
168 | 27 | Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const { |
169 | 27 | Precision result(*this); // copy constructor |
170 | 27 | result.fTrailingZeroDisplay = trailingZeroDisplay; |
171 | 27 | return result; |
172 | 27 | } |
173 | | |
174 | 15.6k | IncrementPrecision Precision::increment(double roundingIncrement) { |
175 | 15.6k | if (roundingIncrement > 0.0) { |
176 | 15.6k | DecimalQuantity dq; |
177 | 15.6k | dq.setToDouble(roundingIncrement); |
178 | 15.6k | dq.roundToInfinity(); |
179 | 15.6k | int32_t magnitude = dq.adjustToZeroScale(); |
180 | 15.6k | return constructIncrement(dq.toLong(), magnitude); |
181 | 15.6k | } else { |
182 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
183 | 0 | } |
184 | 15.6k | } |
185 | | |
186 | 0 | IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) { |
187 | 0 | if (mantissa > 0.0) { |
188 | 0 | return constructIncrement(mantissa, magnitude); |
189 | 0 | } else { |
190 | 0 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | 0 | CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) { |
195 | 0 | return constructCurrency(currencyUsage); |
196 | 0 | } |
197 | | |
198 | | Precision FractionPrecision::withSignificantDigits( |
199 | | int32_t minSignificantDigits, |
200 | | int32_t maxSignificantDigits, |
201 | 81 | UNumberRoundingPriority priority) const { |
202 | 81 | if (fType == RND_ERROR) { return *this; } // no-op in error state |
203 | 80 | if (minSignificantDigits >= 1 && |
204 | 80 | maxSignificantDigits >= minSignificantDigits && |
205 | 80 | maxSignificantDigits <= kMaxIntFracSig) { |
206 | 58 | return constructFractionSignificant( |
207 | 58 | *this, |
208 | 58 | minSignificantDigits, |
209 | 58 | maxSignificantDigits, |
210 | 58 | priority, |
211 | 58 | false); |
212 | 58 | } else { |
213 | 22 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
214 | 22 | } |
215 | 80 | } |
216 | | |
217 | 2.87k | Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const { |
218 | 2.87k | if (fType == RND_ERROR) { return *this; } // no-op in error state |
219 | 2.87k | if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { |
220 | 2.87k | return constructFractionSignificant( |
221 | 2.87k | *this, |
222 | 2.87k | 1, |
223 | 2.87k | minSignificantDigits, |
224 | 2.87k | UNUM_ROUNDING_PRIORITY_RELAXED, |
225 | 2.87k | true); |
226 | 2.87k | } else { |
227 | 2 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
228 | 2 | } |
229 | 2.87k | } |
230 | | |
231 | 58 | Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const { |
232 | 58 | if (fType == RND_ERROR) { return *this; } // no-op in error state |
233 | 48 | if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { |
234 | 38 | return constructFractionSignificant(*this, |
235 | 38 | 1, |
236 | 38 | maxSignificantDigits, |
237 | 38 | UNUM_ROUNDING_PRIORITY_STRICT, |
238 | 38 | true); |
239 | 38 | } else { |
240 | 10 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
241 | 10 | } |
242 | 48 | } |
243 | | |
244 | | // Private method on base class |
245 | 29.5k | Precision Precision::withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const { |
246 | 29.5k | if (fType == RND_ERROR) { return *this; } // no-op in error state |
247 | 29.5k | U_ASSERT(fType == RND_CURRENCY); |
248 | 29.5k | const char16_t *isoCode = currency.getISOCurrency(); |
249 | 29.5k | double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status); |
250 | 29.5k | int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage( |
251 | 29.5k | isoCode, fUnion.currencyUsage, &status); |
252 | 29.5k | Precision retval = (increment != 0.0) |
253 | 29.5k | ? Precision::increment(increment) |
254 | 29.5k | : static_cast<Precision>(Precision::fixedFraction(minMaxFrac)); |
255 | 29.5k | retval.fTrailingZeroDisplay = fTrailingZeroDisplay; |
256 | 29.5k | return retval; |
257 | 29.5k | } |
258 | | |
259 | | // Public method on CurrencyPrecision subclass |
260 | 0 | Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const { |
261 | 0 | UErrorCode localStatus = U_ZERO_ERROR; |
262 | 0 | Precision result = Precision::withCurrency(currency, localStatus); |
263 | 0 | if (U_FAILURE(localStatus)) { |
264 | 0 | return {localStatus}; |
265 | 0 | } |
266 | 0 | return result; |
267 | 0 | } |
268 | | |
269 | 15.6k | Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { |
270 | 15.6k | if (fType == RND_ERROR) { return *this; } // no-op in error state |
271 | 15.6k | if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { |
272 | 15.6k | IncrementPrecision copy = *this; |
273 | 15.6k | copy.fUnion.increment.fMinFrac = minFrac; |
274 | 15.6k | return copy; |
275 | 15.6k | } else { |
276 | 35 | return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; |
277 | 35 | } |
278 | 15.6k | } |
279 | | |
280 | 515k | FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) { |
281 | 515k | FractionSignificantSettings settings{}; |
282 | 515k | settings.fMinFrac = static_cast<digits_t>(minFrac); |
283 | 515k | settings.fMaxFrac = static_cast<digits_t>(maxFrac); |
284 | 515k | settings.fMinSig = -1; |
285 | 515k | settings.fMaxSig = -1; |
286 | 515k | PrecisionUnion union_{}; |
287 | 515k | union_.fracSig = settings; |
288 | 515k | return {RND_FRACTION, union_}; |
289 | 515k | } |
290 | | |
291 | 5.07k | Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) { |
292 | 5.07k | FractionSignificantSettings settings{}; |
293 | 5.07k | settings.fMinFrac = -1; |
294 | 5.07k | settings.fMaxFrac = -1; |
295 | 5.07k | settings.fMinSig = static_cast<digits_t>(minSig); |
296 | 5.07k | settings.fMaxSig = static_cast<digits_t>(maxSig); |
297 | 5.07k | PrecisionUnion union_{}; |
298 | 5.07k | union_.fracSig = settings; |
299 | 5.07k | return {RND_SIGNIFICANT, union_}; |
300 | 5.07k | } |
301 | | |
302 | | Precision |
303 | | Precision::constructFractionSignificant( |
304 | | const FractionPrecision &base, |
305 | | int32_t minSig, |
306 | | int32_t maxSig, |
307 | | UNumberRoundingPriority priority, |
308 | 2.97k | bool retain) { |
309 | 2.97k | FractionSignificantSettings settings = base.fUnion.fracSig; |
310 | 2.97k | settings.fMinSig = static_cast<digits_t>(minSig); |
311 | 2.97k | settings.fMaxSig = static_cast<digits_t>(maxSig); |
312 | 2.97k | settings.fPriority = priority; |
313 | 2.97k | settings.fRetain = retain; |
314 | 2.97k | PrecisionUnion union_{}; |
315 | 2.97k | union_.fracSig = settings; |
316 | 2.97k | return {RND_FRACTION_SIGNIFICANT, union_}; |
317 | 2.97k | } |
318 | | |
319 | 15.6k | IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) { |
320 | 15.6k | IncrementSettings settings{}; |
321 | | // Note: For number formatting, fIncrement is used for RND_INCREMENT but not |
322 | | // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all |
323 | | // three when constructing a skeleton. |
324 | 15.6k | settings.fIncrement = increment; |
325 | 15.6k | settings.fIncrementMagnitude = magnitude; |
326 | 15.6k | settings.fMinFrac = magnitude > 0 ? 0 : -magnitude; |
327 | 15.6k | PrecisionUnion union_{}; |
328 | 15.6k | union_.increment = settings; |
329 | 15.6k | if (increment == 1) { |
330 | | // NOTE: In C++, we must return the correct value type with the correct union. |
331 | | // It would be invalid to return a RND_FRACTION here because the methods on the |
332 | | // IncrementPrecision type assume that the union is backed by increment data. |
333 | 1.08k | return {RND_INCREMENT_ONE, union_}; |
334 | 14.6k | } else if (increment == 5) { |
335 | 259 | return {RND_INCREMENT_FIVE, union_}; |
336 | 14.3k | } else { |
337 | 14.3k | return {RND_INCREMENT, union_}; |
338 | 14.3k | } |
339 | 15.6k | } |
340 | | |
341 | 29.5k | CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { |
342 | 29.5k | PrecisionUnion union_{}; |
343 | 29.5k | union_.currencyUsage = usage; |
344 | 29.5k | return {RND_CURRENCY, union_}; |
345 | 29.5k | } |
346 | | |
347 | | |
348 | | RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode, |
349 | | const CurrencyUnit& currency, UErrorCode& status) |
350 | 11.5k | : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) { |
351 | 11.5k | if (precision.fType == Precision::RND_CURRENCY) { |
352 | 0 | fPrecision = precision.withCurrency(currency, status); |
353 | 0 | } |
354 | 11.5k | } |
355 | | |
356 | 3.55k | RoundingImpl RoundingImpl::passThrough() { |
357 | 3.55k | return {}; |
358 | 3.55k | } |
359 | | |
360 | 0 | bool RoundingImpl::isSignificantDigits() const { |
361 | 0 | return fPrecision.fType == Precision::RND_SIGNIFICANT; |
362 | 0 | } |
363 | | |
364 | | int32_t |
365 | | RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, |
366 | 3.47k | UErrorCode &status) { |
367 | | // Do not call this method with zero, NaN, or infinity. |
368 | 3.47k | U_ASSERT(!input.isZeroish()); |
369 | | |
370 | | // Perform the first attempt at rounding. |
371 | 3.47k | int magnitude = input.getMagnitude(); |
372 | 3.47k | int multiplier = producer.getMultiplier(magnitude); |
373 | 3.47k | input.adjustMagnitude(multiplier); |
374 | 3.47k | apply(input, status); |
375 | | |
376 | | // If the number rounded to zero, exit. |
377 | 3.47k | if (input.isZeroish() || U_FAILURE(status)) { |
378 | 24 | return multiplier; |
379 | 24 | } |
380 | | |
381 | | // If the new magnitude after rounding is the same as it was before rounding, then we are done. |
382 | | // This case applies to most numbers. |
383 | 3.45k | if (input.getMagnitude() == magnitude + multiplier) { |
384 | 3.34k | return multiplier; |
385 | 3.34k | } |
386 | | |
387 | | // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000: |
388 | | // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't, |
389 | | // we do not need to make any more adjustments. |
390 | 109 | int _multiplier = producer.getMultiplier(magnitude + 1); |
391 | 109 | if (multiplier == _multiplier) { |
392 | 65 | return multiplier; |
393 | 65 | } |
394 | | |
395 | | // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000". |
396 | | // Fix the magnitude and re-apply the rounding strategy. |
397 | 44 | input.adjustMagnitude(_multiplier - multiplier); |
398 | 44 | apply(input, status); |
399 | 44 | return _multiplier; |
400 | 109 | } |
401 | | |
402 | | /** This is the method that contains the actual rounding logic. */ |
403 | 1.69M | void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const { |
404 | 1.69M | if (U_FAILURE(status)) { |
405 | 0 | return; |
406 | 0 | } |
407 | 1.69M | if (fPassThrough) { |
408 | 3.55k | return; |
409 | 3.55k | } |
410 | 1.68M | int32_t resolvedMinFraction = 0; |
411 | 1.68M | switch (fPrecision.fType) { |
412 | 0 | case Precision::RND_BOGUS: |
413 | 0 | case Precision::RND_ERROR: |
414 | | // Errors should be caught before the apply() method is called |
415 | 0 | status = U_INTERNAL_PROGRAM_ERROR; |
416 | 0 | break; |
417 | | |
418 | 403 | case Precision::RND_NONE: |
419 | 403 | value.roundToInfinity(); |
420 | 403 | break; |
421 | | |
422 | 1.68M | case Precision::RND_FRACTION: |
423 | 1.68M | value.roundToMagnitude( |
424 | 1.68M | getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac), |
425 | 1.68M | fRoundingMode, |
426 | 1.68M | status); |
427 | 1.68M | resolvedMinFraction = |
428 | 1.68M | uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)); |
429 | 1.68M | break; |
430 | | |
431 | 689 | case Precision::RND_SIGNIFICANT: |
432 | 689 | value.roundToMagnitude( |
433 | 689 | getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig), |
434 | 689 | fRoundingMode, |
435 | 689 | status); |
436 | 689 | resolvedMinFraction = |
437 | 689 | uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)); |
438 | | // Make sure that digits are displayed on zero. |
439 | 689 | if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) { |
440 | 17 | value.increaseMinIntegerTo(1); |
441 | 17 | } |
442 | 689 | break; |
443 | | |
444 | 4.26k | case Precision::RND_FRACTION_SIGNIFICANT: { |
445 | | // From ECMA-402: |
446 | | /* |
447 | | Let sResult be ToRawPrecision(...). |
448 | | Let fResult be ToRawFixed(...). |
449 | | If intlObj.[[RoundingType]] is morePrecision, then |
450 | | If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then |
451 | | Let result be sResult. |
452 | | Else, |
453 | | Let result be fResult. |
454 | | Else, |
455 | | Assert: intlObj.[[RoundingType]] is lessPrecision. |
456 | | If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then |
457 | | Let result be fResult. |
458 | | Else, |
459 | | Let result be sResult. |
460 | | */ |
461 | | |
462 | 4.26k | int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac); |
463 | 4.26k | int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig); |
464 | 4.26k | int32_t roundingMag; |
465 | 4.26k | if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { |
466 | 4.09k | roundingMag = uprv_min(roundingMag1, roundingMag2); |
467 | 4.09k | } else { |
468 | 168 | roundingMag = uprv_max(roundingMag1, roundingMag2); |
469 | 168 | } |
470 | 4.26k | if (!value.isZeroish()) { |
471 | 4.16k | int32_t upperMag = value.getMagnitude(); |
472 | 4.16k | value.roundToMagnitude(roundingMag, fRoundingMode, status); |
473 | 4.16k | if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) { |
474 | | // roundingMag2 needs to be the magnitude after rounding |
475 | 12 | roundingMag2 += 1; |
476 | 12 | } |
477 | 4.16k | } |
478 | | |
479 | 4.26k | int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac); |
480 | 4.26k | int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig); |
481 | 4.26k | int32_t displayMag; |
482 | 4.26k | if (fPrecision.fUnion.fracSig.fRetain) { |
483 | | // withMinDigits + withMaxDigits |
484 | 4.13k | displayMag = uprv_min(displayMag1, displayMag2); |
485 | 4.13k | } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) { |
486 | 38 | if (roundingMag2 <= roundingMag1) { |
487 | 15 | displayMag = displayMag2; |
488 | 23 | } else { |
489 | 23 | displayMag = displayMag1; |
490 | 23 | } |
491 | 93 | } else { |
492 | 93 | U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT); |
493 | 93 | if (roundingMag2 <= roundingMag1) { |
494 | 46 | displayMag = displayMag1; |
495 | 47 | } else { |
496 | 47 | displayMag = displayMag2; |
497 | 47 | } |
498 | 93 | } |
499 | 4.26k | resolvedMinFraction = uprv_max(0, -displayMag); |
500 | | |
501 | 4.26k | break; |
502 | 0 | } |
503 | | |
504 | 0 | case Precision::RND_INCREMENT: |
505 | 0 | value.roundToIncrement( |
506 | 0 | fPrecision.fUnion.increment.fIncrement, |
507 | 0 | fPrecision.fUnion.increment.fIncrementMagnitude, |
508 | 0 | fRoundingMode, |
509 | 0 | status); |
510 | 0 | resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; |
511 | 0 | break; |
512 | | |
513 | 0 | case Precision::RND_INCREMENT_ONE: |
514 | 0 | value.roundToMagnitude( |
515 | 0 | fPrecision.fUnion.increment.fIncrementMagnitude, |
516 | 0 | fRoundingMode, |
517 | 0 | status); |
518 | 0 | resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; |
519 | 0 | break; |
520 | | |
521 | 0 | case Precision::RND_INCREMENT_FIVE: |
522 | 0 | value.roundToNickel( |
523 | 0 | fPrecision.fUnion.increment.fIncrementMagnitude, |
524 | 0 | fRoundingMode, |
525 | 0 | status); |
526 | 0 | resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac; |
527 | 0 | break; |
528 | | |
529 | 0 | case Precision::RND_CURRENCY: |
530 | | // Call .withCurrency() before .apply()! |
531 | 0 | UPRV_UNREACHABLE_EXIT; |
532 | | |
533 | 0 | default: |
534 | 0 | UPRV_UNREACHABLE_EXIT; |
535 | 1.68M | } |
536 | | |
537 | 1.68M | if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO || |
538 | | // PLURAL_OPERAND_T returns fraction digits as an integer |
539 | 1.68M | value.getPluralOperand(PLURAL_OPERAND_T) != 0) { |
540 | 1.68M | value.setMinFraction(resolvedMinFraction); |
541 | 1.68M | } |
542 | 1.68M | } |
543 | | |
544 | 0 | void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) { |
545 | | // This method is intended for the one specific purpose of helping print "00.000E0". |
546 | | // Question: Is it useful to look at trailingZeroDisplay here? |
547 | 0 | U_ASSERT(isSignificantDigits()); |
548 | 0 | U_ASSERT(value.isZeroish()); |
549 | 0 | value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt); |
550 | 0 | } |
551 | | |
552 | | #endif /* #if !UCONFIG_NO_FORMATTING */ |