1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
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"
17 using namespace icu::number
;
18 using namespace icu::number::impl
;
21 using double_conversion::DoubleToStringConverter
;
25 int32_t getRoundingMagnitudeFraction(int maxFrac
) {
32 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity
&value
, int maxSig
) {
36 int magnitude
= value
.isZero() ? 0 : value
.getMagnitude();
37 return magnitude
- maxSig
+ 1;
40 int32_t getDisplayMagnitudeFraction(int minFrac
) {
47 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity
&value
, int minSig
) {
48 int magnitude
= value
.isZero() ? 0 : value
.getMagnitude();
49 return magnitude
- minSig
+ 1;
55 MultiplierProducer::~MultiplierProducer() = default;
58 digits_t
roundingutils::doubleFractionLength(double input
, int8_t* singleDigit
) {
59 char buffer
[DoubleToStringConverter::kBase10MaximalLength
+ 1];
60 bool sign
; // unused; always positive
63 DoubleToStringConverter::DoubleToAscii(
65 DoubleToStringConverter::DtoaMode::SHORTEST
,
74 if (singleDigit
== nullptr) {
76 } else if (length
== 1) {
77 *singleDigit
= buffer
[0] - '0';
82 return static_cast<digits_t
>(length
- point
);
86 Precision
Precision::unlimited() {
87 return Precision(RND_NONE
, {}, kDefaultMode
);
90 FractionPrecision
Precision::integer() {
91 return constructFraction(0, 0);
94 FractionPrecision
Precision::fixedFraction(int32_t minMaxFractionPlaces
) {
95 if (minMaxFractionPlaces
>= 0 && minMaxFractionPlaces
<= kMaxIntFracSig
) {
96 return constructFraction(minMaxFractionPlaces
, minMaxFractionPlaces
);
98 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
102 FractionPrecision
Precision::minFraction(int32_t minFractionPlaces
) {
103 if (minFractionPlaces
>= 0 && minFractionPlaces
<= kMaxIntFracSig
) {
104 return constructFraction(minFractionPlaces
, -1);
106 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
110 FractionPrecision
Precision::maxFraction(int32_t maxFractionPlaces
) {
111 if (maxFractionPlaces
>= 0 && maxFractionPlaces
<= kMaxIntFracSig
) {
112 return constructFraction(0, maxFractionPlaces
);
114 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
118 FractionPrecision
Precision::minMaxFraction(int32_t minFractionPlaces
, int32_t maxFractionPlaces
) {
119 if (minFractionPlaces
>= 0 && maxFractionPlaces
<= kMaxIntFracSig
&&
120 minFractionPlaces
<= maxFractionPlaces
) {
121 return constructFraction(minFractionPlaces
, maxFractionPlaces
);
123 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
127 Precision
Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits
) {
128 if (minMaxSignificantDigits
>= 1 && minMaxSignificantDigits
<= kMaxIntFracSig
) {
129 return constructSignificant(minMaxSignificantDigits
, minMaxSignificantDigits
);
131 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
135 Precision
Precision::minSignificantDigits(int32_t minSignificantDigits
) {
136 if (minSignificantDigits
>= 1 && minSignificantDigits
<= kMaxIntFracSig
) {
137 return constructSignificant(minSignificantDigits
, -1);
139 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
143 Precision
Precision::maxSignificantDigits(int32_t maxSignificantDigits
) {
144 if (maxSignificantDigits
>= 1 && maxSignificantDigits
<= kMaxIntFracSig
) {
145 return constructSignificant(1, maxSignificantDigits
);
147 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
151 Precision
Precision::minMaxSignificantDigits(int32_t minSignificantDigits
, int32_t maxSignificantDigits
) {
152 if (minSignificantDigits
>= 1 && maxSignificantDigits
<= kMaxIntFracSig
&&
153 minSignificantDigits
<= maxSignificantDigits
) {
154 return constructSignificant(minSignificantDigits
, maxSignificantDigits
);
156 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
160 IncrementPrecision
Precision::increment(double roundingIncrement
) {
161 if (roundingIncrement
> 0.0) {
162 return constructIncrement(roundingIncrement
, 0);
164 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
168 CurrencyPrecision
Precision::currency(UCurrencyUsage currencyUsage
) {
169 return constructCurrency(currencyUsage
);
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);
177 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
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
);
186 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
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
);
201 return constructFraction(minMaxFrac
, minMaxFrac
);
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
};
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
);
220 return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR
};
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
};
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
};
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
};
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
};
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)
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
};
287 return {RND_INCREMENT
, union_
, kDefaultMode
};
291 CurrencyPrecision
Precision::constructCurrency(UCurrencyUsage usage
) {
292 PrecisionUnion union_
;
293 union_
.currencyUsage
= usage
;
294 return {RND_CURRENCY
, union_
, kDefaultMode
};
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
);
306 RoundingImpl
RoundingImpl::passThrough() {
308 retval
.fPassThrough
= true;
312 bool RoundingImpl::isSignificantDigits() const {
313 return fPrecision
.fType
== Precision::RND_SIGNIFICANT
;
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());
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
);
328 // If the number rounded to zero, exit.
329 if (input
.isZero() || U_FAILURE(status
)) {
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
) {
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
) {
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
);
354 /** This is the method that contains the actual rounding logic. */
355 void RoundingImpl::apply(impl::DecimalQuantity
&value
, UErrorCode
& status
) const {
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
;
366 case Precision::RND_NONE
:
367 value
.roundToInfinity();
370 case Precision::RND_FRACTION
:
371 value
.roundToMagnitude(
372 getRoundingMagnitudeFraction(fPrecision
.fUnion
.fracSig
.fMaxFrac
),
375 value
.setMinFraction(
376 uprv_max(0, -getDisplayMagnitudeFraction(fPrecision
.fUnion
.fracSig
.fMinFrac
)));
379 case Precision::RND_SIGNIFICANT
:
380 value
.roundToMagnitude(
381 getRoundingMagnitudeSignificant(value
, fPrecision
.fUnion
.fracSig
.fMaxSig
),
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);
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) {
397 int32_t candidate
= getRoundingMagnitudeSignificant(
399 fPrecision
.fUnion
.fracSig
.fMaxSig
);
400 roundingMag
= uprv_max(roundingMag
, candidate
);
403 int32_t candidate
= getDisplayMagnitudeSignificant(
405 fPrecision
.fUnion
.fracSig
.fMinSig
);
406 roundingMag
= uprv_min(roundingMag
, candidate
);
408 value
.roundToMagnitude(roundingMag
, fRoundingMode
, status
);
409 value
.setMinFraction(uprv_max(0, -displayMag
));
413 case Precision::RND_INCREMENT
:
414 value
.roundToIncrement(
415 fPrecision
.fUnion
.increment
.fIncrement
,
418 value
.setMinFraction(fPrecision
.fUnion
.increment
.fMinFrac
);
421 case Precision::RND_INCREMENT_ONE
:
422 value
.roundToMagnitude(
423 -fPrecision
.fUnion
.increment
.fMaxFrac
,
426 value
.setMinFraction(fPrecision
.fUnion
.increment
.fMinFrac
);
429 case Precision::RND_INCREMENT_FIVE
:
431 -fPrecision
.fUnion
.increment
.fMaxFrac
,
434 value
.setMinFraction(fPrecision
.fUnion
.increment
.fMinFrac
);
437 case Precision::RND_CURRENCY
:
438 // Call .withCurrency() before .apply()!
441 case Precision::RND_INCREMENT_SIGNIFICANT
: // Apple addition rdar://52538227
442 // FIrst round to increment
443 value
.roundToIncrement(
444 fPrecision
.fUnion
.incrSig
.fIncrement
,
447 // Then round to significant digits
448 value
.roundToMagnitude(
449 getRoundingMagnitudeSignificant(value
, fPrecision
.fUnion
.incrSig
.fMaxSig
),
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);
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
);
472 #endif /* #if !UCONFIG_NO_FORMATTING */