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