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