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 && !UPRV_INCOMPLETE_CPP11_SUPPORT 7 8 #include "uassert.h" 9 #include "unicode/numberformatter.h" 10 #include "number_types.h" 11 #include "number_decimalquantity.h" 12 13 using namespace icu; 14 using namespace icu::number; 15 using namespace icu::number::impl; 16 17 namespace { 18 19 int32_t getRoundingMagnitudeFraction(int maxFrac) { 20 if (maxFrac == -1) { 21 return INT32_MIN; 22 } 23 return -maxFrac; 24 } 25 26 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) { 27 if (maxSig == -1) { 28 return INT32_MIN; 29 } 30 int magnitude = value.isZero() ? 0 : value.getMagnitude(); 31 return magnitude - maxSig + 1; 32 } 33 34 int32_t getDisplayMagnitudeFraction(int minFrac) { 35 if (minFrac == 0) { 36 return INT32_MAX; 37 } 38 return -minFrac; 39 } 40 41 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) { 42 int magnitude = value.isZero() ? 0 : value.getMagnitude(); 43 return magnitude - minSig + 1; 44 } 45 46 } 47 48 49 Rounder Rounder::unlimited() { 50 return Rounder(RND_NONE, {}, kDefaultMode); 51 } 52 53 FractionRounder Rounder::integer() { 54 return constructFraction(0, 0); 55 } 56 57 FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) { 58 if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { 59 return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); 60 } else { 61 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 62 } 63 } 64 65 FractionRounder Rounder::minFraction(int32_t minFractionPlaces) { 66 if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { 67 return constructFraction(minFractionPlaces, -1); 68 } else { 69 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 70 } 71 } 72 73 FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) { 74 if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { 75 return constructFraction(0, maxFractionPlaces); 76 } else { 77 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 78 } 79 } 80 81 FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { 82 if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig && 83 minFractionPlaces <= maxFractionPlaces) { 84 return constructFraction(minFractionPlaces, maxFractionPlaces); 85 } else { 86 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 87 } 88 } 89 90 Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) { 91 if (minMaxSignificantDigits >= 0 && minMaxSignificantDigits <= kMaxIntFracSig) { 92 return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); 93 } else { 94 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 95 } 96 } 97 98 Rounder Rounder::minDigits(int32_t minSignificantDigits) { 99 if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) { 100 return constructSignificant(minSignificantDigits, -1); 101 } else { 102 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 103 } 104 } 105 106 Rounder Rounder::maxDigits(int32_t maxSignificantDigits) { 107 if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) { 108 return constructSignificant(0, maxSignificantDigits); 109 } else { 110 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 111 } 112 } 113 114 Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { 115 if (minSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig && 116 minSignificantDigits <= maxSignificantDigits) { 117 return constructSignificant(minSignificantDigits, maxSignificantDigits); 118 } else { 119 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 120 } 121 } 122 123 IncrementRounder Rounder::increment(double roundingIncrement) { 124 if (roundingIncrement > 0.0) { 125 return constructIncrement(roundingIncrement, 0); 126 } else { 127 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 128 } 129 } 130 131 CurrencyRounder Rounder::currency(UCurrencyUsage currencyUsage) { 132 return constructCurrency(currencyUsage); 133 } 134 135 Rounder Rounder::withMode(RoundingMode roundingMode) const { 136 if (fType == RND_ERROR) { return *this; } // no-op in error state 137 return {fType, fUnion, roundingMode}; 138 } 139 140 Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const { 141 if (fType == RND_ERROR) { return *this; } // no-op in error state 142 if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) { 143 return constructFractionSignificant(*this, minSignificantDigits, -1); 144 } else { 145 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 146 } 147 } 148 149 Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const { 150 if (fType == RND_ERROR) { return *this; } // no-op in error state 151 if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) { 152 return constructFractionSignificant(*this, -1, maxSignificantDigits); 153 } else { 154 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 155 } 156 } 157 158 // Private method on base class 159 Rounder Rounder::withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const { 160 if (fType == RND_ERROR) { return *this; } // no-op in error state 161 U_ASSERT(fType == RND_CURRENCY); 162 const char16_t *isoCode = currency.getISOCurrency(); 163 double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status); 164 int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage( 165 isoCode, fUnion.currencyUsage, &status); 166 if (increment != 0.0) { 167 return constructIncrement(increment, minMaxFrac); 168 } else { 169 return constructFraction(minMaxFrac, minMaxFrac); 170 } 171 } 172 173 // Public method on CurrencyRounder subclass 174 Rounder CurrencyRounder::withCurrency(const CurrencyUnit ¤cy) const { 175 UErrorCode localStatus = U_ZERO_ERROR; 176 Rounder result = Rounder::withCurrency(currency, localStatus); 177 if (U_FAILURE(localStatus)) { 178 return {localStatus}; 179 } 180 return result; 181 } 182 183 Rounder IncrementRounder::withMinFraction(int32_t minFrac) const { 184 if (fType == RND_ERROR) { return *this; } // no-op in error state 185 if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { 186 return constructIncrement(fUnion.increment.fIncrement, minFrac); 187 } else { 188 return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; 189 } 190 } 191 192 FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) { 193 FractionSignificantSettings settings; 194 settings.fMinFrac = static_cast<int8_t> (minFrac); 195 settings.fMaxFrac = static_cast<int8_t> (maxFrac); 196 settings.fMinSig = -1; 197 settings.fMaxSig = -1; 198 RounderUnion union_; 199 union_.fracSig = settings; 200 return {RND_FRACTION, union_, kDefaultMode}; 201 } 202 203 Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) { 204 FractionSignificantSettings settings; 205 settings.fMinFrac = -1; 206 settings.fMaxFrac = -1; 207 settings.fMinSig = static_cast<int8_t>(minSig); 208 settings.fMaxSig = static_cast<int8_t>(maxSig); 209 RounderUnion union_; 210 union_.fracSig = settings; 211 return {RND_SIGNIFICANT, union_, kDefaultMode}; 212 } 213 214 Rounder 215 Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) { 216 FractionSignificantSettings settings = base.fUnion.fracSig; 217 settings.fMinSig = static_cast<int8_t>(minSig); 218 settings.fMaxSig = static_cast<int8_t>(maxSig); 219 RounderUnion union_; 220 union_.fracSig = settings; 221 return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode}; 222 } 223 224 IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) { 225 IncrementSettings settings; 226 settings.fIncrement = increment; 227 settings.fMinFrac = minFrac; 228 RounderUnion union_; 229 union_.increment = settings; 230 return {RND_INCREMENT, union_, kDefaultMode}; 231 } 232 233 CurrencyRounder Rounder::constructCurrency(UCurrencyUsage usage) { 234 RounderUnion union_; 235 union_.currencyUsage = usage; 236 return {RND_CURRENCY, union_, kDefaultMode}; 237 } 238 239 Rounder Rounder::constructPassThrough() { 240 RounderUnion union_; 241 union_.errorCode = U_ZERO_ERROR; // initialize the variable 242 return {RND_PASS_THROUGH, union_, kDefaultMode}; 243 } 244 245 void Rounder::setLocaleData(const CurrencyUnit ¤cy, UErrorCode &status) { 246 if (fType == RND_CURRENCY) { 247 *this = withCurrency(currency, status); 248 } 249 } 250 251 int32_t 252 Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, 253 UErrorCode &status) { 254 // TODO: Make a better and more efficient implementation. 255 // TODO: Avoid the object creation here. 256 DecimalQuantity copy(input); 257 258 U_ASSERT(!input.isZero()); 259 int32_t magnitude = input.getMagnitude(); 260 int32_t multiplier = producer.getMultiplier(magnitude); 261 input.adjustMagnitude(multiplier); 262 apply(input, status); 263 264 // If the number turned to zero when rounding, do not re-attempt the rounding. 265 if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) { 266 magnitude += 1; 267 input = copy; 268 multiplier = producer.getMultiplier(magnitude); 269 input.adjustMagnitude(multiplier); 270 U_ASSERT(input.getMagnitude() == magnitude + multiplier - 1); 271 apply(input, status); 272 U_ASSERT(input.getMagnitude() == magnitude + multiplier); 273 } 274 275 return multiplier; 276 } 277 278 /** This is the method that contains the actual rounding logic. */ 279 void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const { 280 switch (fType) { 281 case RND_BOGUS: 282 case RND_ERROR: 283 // Errors should be caught before the apply() method is called 284 status = U_INTERNAL_PROGRAM_ERROR; 285 break; 286 287 case RND_NONE: 288 value.roundToInfinity(); 289 break; 290 291 case RND_FRACTION: 292 value.roundToMagnitude( 293 getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac), fRoundingMode, status); 294 value.setFractionLength( 295 uprv_max(0, -getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac)), INT32_MAX); 296 break; 297 298 case RND_SIGNIFICANT: 299 value.roundToMagnitude( 300 getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig), 301 fRoundingMode, 302 status); 303 value.setFractionLength( 304 uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)), 305 INT32_MAX); 306 break; 307 308 case RND_FRACTION_SIGNIFICANT: { 309 int32_t displayMag = getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac); 310 int32_t roundingMag = getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac); 311 if (fUnion.fracSig.fMinSig == -1) { 312 // Max Sig override 313 int32_t candidate = getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig); 314 roundingMag = uprv_max(roundingMag, candidate); 315 } else { 316 // Min Sig override 317 int32_t candidate = getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig); 318 roundingMag = uprv_min(roundingMag, candidate); 319 } 320 value.roundToMagnitude(roundingMag, fRoundingMode, status); 321 value.setFractionLength(uprv_max(0, -displayMag), INT32_MAX); 322 break; 323 } 324 325 case RND_INCREMENT: 326 value.roundToIncrement( 327 fUnion.increment.fIncrement, fRoundingMode, fUnion.increment.fMinFrac, status); 328 value.setFractionLength(fUnion.increment.fMinFrac, fUnion.increment.fMinFrac); 329 break; 330 331 case RND_CURRENCY: 332 // Call .withCurrency() before .apply()! 333 U_ASSERT(false); 334 335 case RND_PASS_THROUGH: 336 break; 337 } 338 } 339 340 void Rounder::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) { 341 // This method is intended for the one specific purpose of helping print "00.000E0". 342 U_ASSERT(fType == RND_SIGNIFICANT); 343 U_ASSERT(value.isZero()); 344 value.setFractionLength(fUnion.fracSig.fMinSig - minInt, INT32_MAX); 345 } 346 347 #endif /* #if !UCONFIG_NO_FORMATTING */ 348