Home | History | Annotate | Download | only in i18n
      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 &currency, 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 &currency) 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 &currency, 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