tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

number_rounding.cpp (20281B)


      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 #ifdef JS_HAS_INTL_API
     14 #include "double-conversion/double-conversion.h"
     15 #else
     16 #include "double-conversion.h"
     17 #endif
     18 #include "number_roundingutils.h"
     19 #include "number_skeletons.h"
     20 #include "number_decnum.h"
     21 #include "putilimp.h"
     22 #include "string_segment.h"
     23 
     24 using namespace icu;
     25 using namespace icu::number;
     26 using namespace icu::number::impl;
     27 
     28 
     29 using double_conversion::DoubleToStringConverter;
     30 using icu::StringSegment;
     31 
     32 void number::impl::parseIncrementOption(const StringSegment &segment,
     33                                        Precision &outPrecision,
     34                                        UErrorCode &status) {
     35    // Need to do char <-> char16_t conversion...
     36    U_ASSERT(U_SUCCESS(status));
     37    CharString buffer;
     38    SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
     39 
     40    // Utilize DecimalQuantity/decNumber to parse this for us.
     41    DecimalQuantity dq;
     42    UErrorCode localStatus = U_ZERO_ERROR;
     43    dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
     44    if (U_FAILURE(localStatus) || dq.isNaN() || dq.isInfinite()) {
     45        // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
     46        status = U_NUMBER_SKELETON_SYNTAX_ERROR;
     47        return;
     48    }
     49    // Now we break apart the number into a mantissa and exponent (magnitude).
     50    int32_t magnitude = dq.adjustToZeroScale();
     51    // setToDecNumber drops trailing zeros, so we search for the '.' manually.
     52    for (int32_t i=0; i<buffer.length(); i++) {
     53        if (buffer[i] == '.') {
     54            int32_t newMagnitude = i - buffer.length() + 1;
     55            dq.adjustMagnitude(magnitude - newMagnitude);
     56            magnitude = newMagnitude;
     57            break;
     58        }
     59    }
     60    outPrecision = Precision::incrementExact(dq.toLong(), magnitude);
     61 }
     62 
     63 namespace {
     64 
     65 int32_t getRoundingMagnitudeFraction(int maxFrac) {
     66    if (maxFrac == -1) {
     67        return INT32_MIN;
     68    }
     69    return -maxFrac;
     70 }
     71 
     72 int32_t getRoundingMagnitudeSignificant(const DecimalQuantity &value, int maxSig) {
     73    if (maxSig == -1) {
     74        return INT32_MIN;
     75    }
     76    int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
     77    return magnitude - maxSig + 1;
     78 }
     79 
     80 int32_t getDisplayMagnitudeFraction(int minFrac) {
     81    if (minFrac == 0) {
     82        return INT32_MAX;
     83    }
     84    return -minFrac;
     85 }
     86 
     87 int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) {
     88    int magnitude = value.isZeroish() ? 0 : value.getMagnitude();
     89    return magnitude - minSig + 1;
     90 }
     91 
     92 }
     93 
     94 
     95 MultiplierProducer::~MultiplierProducer() = default;
     96 
     97 
     98 Precision Precision::unlimited() {
     99    return Precision(RND_NONE, {});
    100 }
    101 
    102 FractionPrecision Precision::integer() {
    103    return constructFraction(0, 0);
    104 }
    105 
    106 FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) {
    107    if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) {
    108        return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
    109    } else {
    110        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    111    }
    112 }
    113 
    114 FractionPrecision Precision::minFraction(int32_t minFractionPlaces) {
    115    if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) {
    116        return constructFraction(minFractionPlaces, -1);
    117    } else {
    118        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    119    }
    120 }
    121 
    122 FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) {
    123    if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) {
    124        return constructFraction(0, maxFractionPlaces);
    125    } else {
    126        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    127    }
    128 }
    129 
    130 FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) {
    131    if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig &&
    132        minFractionPlaces <= maxFractionPlaces) {
    133        return constructFraction(minFractionPlaces, maxFractionPlaces);
    134    } else {
    135        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    136    }
    137 }
    138 
    139 Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) {
    140    if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) {
    141        return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
    142    } else {
    143        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    144    }
    145 }
    146 
    147 Precision Precision::minSignificantDigits(int32_t minSignificantDigits) {
    148    if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
    149        return constructSignificant(minSignificantDigits, -1);
    150    } else {
    151        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    152    }
    153 }
    154 
    155 Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) {
    156    if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
    157        return constructSignificant(1, maxSignificantDigits);
    158    } else {
    159        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    160    }
    161 }
    162 
    163 Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) {
    164    if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig &&
    165        minSignificantDigits <= maxSignificantDigits) {
    166        return constructSignificant(minSignificantDigits, maxSignificantDigits);
    167    } else {
    168        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    169    }
    170 }
    171 
    172 Precision Precision::trailingZeroDisplay(UNumberTrailingZeroDisplay trailingZeroDisplay) const {
    173    Precision result(*this); // copy constructor
    174    result.fTrailingZeroDisplay = trailingZeroDisplay;
    175    return result;
    176 }
    177 
    178 IncrementPrecision Precision::increment(double roundingIncrement) {
    179    if (roundingIncrement > 0.0) {
    180        DecimalQuantity dq;
    181        dq.setToDouble(roundingIncrement);
    182        dq.roundToInfinity();
    183        int32_t magnitude = dq.adjustToZeroScale();
    184        return constructIncrement(dq.toLong(), magnitude);
    185    } else {
    186        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    187    }
    188 }
    189 
    190 IncrementPrecision Precision::incrementExact(uint64_t mantissa, int16_t magnitude) {
    191    if (mantissa > 0.0) {
    192        return constructIncrement(mantissa, magnitude);
    193    } else {
    194        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    195    }
    196 }
    197 
    198 CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) {
    199    return constructCurrency(currencyUsage);
    200 }
    201 
    202 Precision FractionPrecision::withSignificantDigits(
    203        int32_t minSignificantDigits,
    204        int32_t maxSignificantDigits,
    205        UNumberRoundingPriority priority) const {
    206    if (fType == RND_ERROR) { return *this; } // no-op in error state
    207    if (minSignificantDigits >= 1 &&
    208            maxSignificantDigits >= minSignificantDigits &&
    209            maxSignificantDigits <= kMaxIntFracSig) {
    210        return constructFractionSignificant(
    211            *this,
    212            minSignificantDigits,
    213            maxSignificantDigits,
    214            priority,
    215            false);
    216    } else {
    217        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    218    }
    219 }
    220 
    221 Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const {
    222    if (fType == RND_ERROR) { return *this; } // no-op in error state
    223    if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) {
    224        return constructFractionSignificant(
    225            *this,
    226            1,
    227            minSignificantDigits,
    228            UNUM_ROUNDING_PRIORITY_RELAXED,
    229            true);
    230    } else {
    231        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    232    }
    233 }
    234 
    235 Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const {
    236    if (fType == RND_ERROR) { return *this; } // no-op in error state
    237    if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) {
    238        return constructFractionSignificant(*this,
    239            1,
    240            maxSignificantDigits,
    241            UNUM_ROUNDING_PRIORITY_STRICT,
    242            true);
    243    } else {
    244        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    245    }
    246 }
    247 
    248 // Private method on base class
    249 Precision Precision::withCurrency(const CurrencyUnit &currency, UErrorCode &status) const {
    250    if (fType == RND_ERROR) { return *this; } // no-op in error state
    251    U_ASSERT(fType == RND_CURRENCY);
    252    const char16_t *isoCode = currency.getISOCurrency();
    253    double increment = ucurr_getRoundingIncrementForUsage(isoCode, fUnion.currencyUsage, &status);
    254    int32_t minMaxFrac = ucurr_getDefaultFractionDigitsForUsage(
    255            isoCode, fUnion.currencyUsage, &status);
    256    Precision retval = (increment != 0.0)
    257        ? Precision::increment(increment)
    258        : static_cast<Precision>(Precision::fixedFraction(minMaxFrac));
    259    retval.fTrailingZeroDisplay = fTrailingZeroDisplay;
    260    return retval;
    261 }
    262 
    263 // Public method on CurrencyPrecision subclass
    264 Precision CurrencyPrecision::withCurrency(const CurrencyUnit &currency) const {
    265    UErrorCode localStatus = U_ZERO_ERROR;
    266    Precision result = Precision::withCurrency(currency, localStatus);
    267    if (U_FAILURE(localStatus)) {
    268        return {localStatus};
    269    }
    270    return result;
    271 }
    272 
    273 Precision IncrementPrecision::withMinFraction(int32_t minFrac) const {
    274    if (fType == RND_ERROR) { return *this; } // no-op in error state
    275    if (minFrac >= 0 && minFrac <= kMaxIntFracSig) {
    276        IncrementPrecision copy = *this;
    277        copy.fUnion.increment.fMinFrac = minFrac;
    278        return copy;
    279    } else {
    280        return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
    281    }
    282 }
    283 
    284 FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) {
    285    FractionSignificantSettings settings{};
    286    settings.fMinFrac = static_cast<digits_t>(minFrac);
    287    settings.fMaxFrac = static_cast<digits_t>(maxFrac);
    288    settings.fMinSig = -1;
    289    settings.fMaxSig = -1;
    290    PrecisionUnion union_{};
    291    union_.fracSig = settings;
    292    return {RND_FRACTION, union_};
    293 }
    294 
    295 Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
    296    FractionSignificantSettings settings{};
    297    settings.fMinFrac = -1;
    298    settings.fMaxFrac = -1;
    299    settings.fMinSig = static_cast<digits_t>(minSig);
    300    settings.fMaxSig = static_cast<digits_t>(maxSig);
    301    PrecisionUnion union_{};
    302    union_.fracSig = settings;
    303    return {RND_SIGNIFICANT, union_};
    304 }
    305 
    306 Precision
    307 Precision::constructFractionSignificant(
    308        const FractionPrecision &base,
    309        int32_t minSig,
    310        int32_t maxSig,
    311        UNumberRoundingPriority priority,
    312        bool retain) {
    313    FractionSignificantSettings settings = base.fUnion.fracSig;
    314    settings.fMinSig = static_cast<digits_t>(minSig);
    315    settings.fMaxSig = static_cast<digits_t>(maxSig);
    316    settings.fPriority = priority;
    317    settings.fRetain = retain;
    318    PrecisionUnion union_{};
    319    union_.fracSig = settings;
    320    return {RND_FRACTION_SIGNIFICANT, union_};
    321 }
    322 
    323 IncrementPrecision Precision::constructIncrement(uint64_t increment, digits_t magnitude) {
    324    IncrementSettings settings{};
    325    // Note: For number formatting, fIncrement is used for RND_INCREMENT but not
    326    // RND_INCREMENT_ONE or RND_INCREMENT_FIVE. However, fIncrement is used in all
    327    // three when constructing a skeleton.
    328    settings.fIncrement = increment;
    329    settings.fIncrementMagnitude = magnitude;
    330    settings.fMinFrac = magnitude > 0 ? 0 : -magnitude;
    331    PrecisionUnion union_{};
    332    union_.increment = settings;
    333    if (increment == 1) {
    334        // NOTE: In C++, we must return the correct value type with the correct union.
    335        // It would be invalid to return a RND_FRACTION here because the methods on the
    336        // IncrementPrecision type assume that the union is backed by increment data.
    337        return {RND_INCREMENT_ONE, union_};
    338    } else if (increment == 5) {
    339        return {RND_INCREMENT_FIVE, union_};
    340    } else {
    341        return {RND_INCREMENT, union_};
    342    }
    343 }
    344 
    345 CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
    346    PrecisionUnion union_{};
    347    union_.currencyUsage = usage;
    348    return {RND_CURRENCY, union_};
    349 }
    350 
    351 
    352 RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
    353                           const CurrencyUnit& currency, UErrorCode& status)
    354        : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) {
    355    if (precision.fType == Precision::RND_CURRENCY) {
    356        fPrecision = precision.withCurrency(currency, status);
    357    }
    358 }
    359 
    360 RoundingImpl RoundingImpl::passThrough() {
    361    return {};
    362 }
    363 
    364 bool RoundingImpl::isSignificantDigits() const {
    365    return fPrecision.fType == Precision::RND_SIGNIFICANT;
    366 }
    367 
    368 int32_t
    369 RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
    370                                  UErrorCode &status) {
    371    // Do not call this method with zero, NaN, or infinity.
    372    U_ASSERT(!input.isZeroish());
    373 
    374    // Perform the first attempt at rounding.
    375    int magnitude = input.getMagnitude();
    376    int multiplier = producer.getMultiplier(magnitude);
    377    input.adjustMagnitude(multiplier);
    378    apply(input, status);
    379 
    380    // If the number rounded to zero, exit.
    381    if (input.isZeroish() || U_FAILURE(status)) {
    382        return multiplier;
    383    }
    384 
    385    // If the new magnitude after rounding is the same as it was before rounding, then we are done.
    386    // This case applies to most numbers.
    387    if (input.getMagnitude() == magnitude + multiplier) {
    388        return multiplier;
    389    }
    390 
    391    // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
    392    // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
    393    // we do not need to make any more adjustments.
    394    int _multiplier = producer.getMultiplier(magnitude + 1);
    395    if (multiplier == _multiplier) {
    396        return multiplier;
    397    }
    398 
    399    // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
    400    // Fix the magnitude and re-apply the rounding strategy.
    401    input.adjustMagnitude(_multiplier - multiplier);
    402    apply(input, status);
    403    return _multiplier;
    404 }
    405 
    406 /** This is the method that contains the actual rounding logic. */
    407 void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
    408    if (U_FAILURE(status)) {
    409        return;
    410    }
    411    if (fPassThrough) {
    412        return;
    413    }
    414    int32_t resolvedMinFraction = 0;
    415    switch (fPrecision.fType) {
    416        case Precision::RND_BOGUS:
    417        case Precision::RND_ERROR:
    418            // Errors should be caught before the apply() method is called
    419            status = U_INTERNAL_PROGRAM_ERROR;
    420            break;
    421 
    422        case Precision::RND_NONE:
    423            value.roundToInfinity();
    424            break;
    425 
    426        case Precision::RND_FRACTION:
    427            value.roundToMagnitude(
    428                    getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac),
    429                    fRoundingMode,
    430                    status);
    431            resolvedMinFraction =
    432                    uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac));
    433            break;
    434 
    435        case Precision::RND_SIGNIFICANT:
    436            value.roundToMagnitude(
    437                    getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig),
    438                    fRoundingMode,
    439                    status);
    440            resolvedMinFraction =
    441                    uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig));
    442            // Make sure that digits are displayed on zero.
    443            if (value.isZeroish() && fPrecision.fUnion.fracSig.fMinSig > 0) {
    444                value.increaseMinIntegerTo(1);
    445            }
    446            break;
    447 
    448        case Precision::RND_FRACTION_SIGNIFICANT: {
    449            // From ECMA-402:
    450            /*
    451            Let sResult be ToRawPrecision(...).
    452            Let fResult be ToRawFixed(...).
    453            If intlObj.[[RoundingType]] is morePrecision, then
    454                If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
    455                    Let result be sResult.
    456                Else,
    457                    Let result be fResult.
    458            Else,
    459                Assert: intlObj.[[RoundingType]] is lessPrecision.
    460                If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
    461                    Let result be fResult.
    462                Else,
    463                    Let result be sResult.
    464            */
    465 
    466            int32_t roundingMag1 = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac);
    467            int32_t roundingMag2 = getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig);
    468            int32_t roundingMag;
    469            if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
    470                roundingMag = uprv_min(roundingMag1, roundingMag2);
    471            } else {
    472                roundingMag = uprv_max(roundingMag1, roundingMag2);
    473            }
    474            if (!value.isZeroish()) {
    475                int32_t upperMag = value.getMagnitude();
    476                value.roundToMagnitude(roundingMag, fRoundingMode, status);
    477                if (!value.isZeroish() && value.getMagnitude() != upperMag && roundingMag1 == roundingMag2) {
    478                    // roundingMag2 needs to be the magnitude after rounding
    479                    roundingMag2 += 1;
    480                }
    481            }
    482 
    483            int32_t displayMag1 = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac);
    484            int32_t displayMag2 = getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig);
    485            int32_t displayMag;
    486            if (fPrecision.fUnion.fracSig.fRetain) {
    487                // withMinDigits + withMaxDigits
    488                displayMag = uprv_min(displayMag1, displayMag2);
    489            } else if (fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
    490                if (roundingMag2 <= roundingMag1) {
    491                    displayMag = displayMag2;
    492                } else {
    493                    displayMag = displayMag1;
    494                }
    495            } else {
    496                U_ASSERT(fPrecision.fUnion.fracSig.fPriority == UNUM_ROUNDING_PRIORITY_STRICT);
    497                if (roundingMag2 <= roundingMag1) {
    498                    displayMag = displayMag1;
    499                } else {
    500                    displayMag = displayMag2;
    501                }
    502            }
    503            resolvedMinFraction = uprv_max(0, -displayMag);
    504 
    505            break;
    506        }
    507 
    508        case Precision::RND_INCREMENT:
    509            value.roundToIncrement(
    510                    fPrecision.fUnion.increment.fIncrement,
    511                    fPrecision.fUnion.increment.fIncrementMagnitude,
    512                    fRoundingMode,
    513                    status);
    514            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
    515            break;
    516 
    517        case Precision::RND_INCREMENT_ONE:
    518            value.roundToMagnitude(
    519                    fPrecision.fUnion.increment.fIncrementMagnitude,
    520                    fRoundingMode,
    521                    status);
    522            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
    523            break;
    524 
    525        case Precision::RND_INCREMENT_FIVE:
    526            value.roundToNickel(
    527                    fPrecision.fUnion.increment.fIncrementMagnitude,
    528                    fRoundingMode,
    529                    status);
    530            resolvedMinFraction = fPrecision.fUnion.increment.fMinFrac;
    531            break;
    532 
    533        case Precision::RND_CURRENCY:
    534            // Call .withCurrency() before .apply()!
    535            UPRV_UNREACHABLE_EXIT;
    536 
    537        default:
    538            UPRV_UNREACHABLE_EXIT;
    539    }
    540 
    541    if (fPrecision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_AUTO ||
    542            // PLURAL_OPERAND_T returns fraction digits as an integer
    543            value.getPluralOperand(PLURAL_OPERAND_T) != 0) {
    544        value.setMinFraction(resolvedMinFraction);
    545    }
    546 }
    547 
    548 void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) {
    549    // This method is intended for the one specific purpose of helping print "00.000E0".
    550    // Question: Is it useful to look at trailingZeroDisplay here?
    551    U_ASSERT(isSignificantDigits());
    552    U_ASSERT(value.isZeroish());
    553    value.setMinFraction(fPrecision.fUnion.fracSig.fMinSig - minInt);
    554 }
    555 
    556 #endif /* #if !UCONFIG_NO_FORMATTING */