| 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 | |