tor-browser

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

number_scientific.cpp (6820B)


      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 <cstdlib>
      9 #include "number_scientific.h"
     10 #include "number_utils.h"
     11 #include "formatted_string_builder.h"
     12 #include "unicode/unum.h"
     13 #include "number_microprops.h"
     14 
     15 using namespace icu;
     16 using namespace icu::number;
     17 using namespace icu::number::impl;
     18 
     19 // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
     20 //
     21 // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
     22 //
     23 // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
     24 // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
     25 // instances.  This scheme reduces the number of object creations by 1 in both safe and unsafe.
     26 //
     27 // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
     28 // the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
     29 
     30 ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
     31 
     32 void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) {
     33    // ScientificModifier should be set only once.
     34    U_ASSERT(fHandler == nullptr);
     35    fExponent = exponent;
     36    fHandler = handler;
     37 }
     38 
     39 int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
     40                                  UErrorCode &status) const {
     41    // FIXME: Localized exponent separator location.
     42    int i = rightIndex;
     43    // Append the exponent separator and sign
     44    i += output.insert(
     45            i,
     46            fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
     47            {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD},
     48            status);
     49    if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) {
     50        i += output.insert(
     51                i,
     52                fHandler->fSymbols
     53                        ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),
     54                {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
     55                status);
     56    } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) {
     57        i += output.insert(
     58                i,
     59                fHandler->fSymbols
     60                        ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),
     61                {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
     62                status);
     63    }
     64    // Append the exponent digits (using a simple inline algorithm)
     65    int32_t disp = std::abs(fExponent);
     66    for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
     67        auto d = static_cast<int8_t>(disp % 10);
     68        i += utils::insertDigitFromSymbols(
     69                output,
     70                i - j,
     71                d,
     72                *fHandler->fSymbols,
     73                {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD},
     74                status);
     75    }
     76    return i - rightIndex;
     77 }
     78 
     79 int32_t ScientificModifier::getPrefixLength() const {
     80    // TODO: Localized exponent separator location.
     81    return 0;
     82 }
     83 
     84 int32_t ScientificModifier::getCodePointCount() const {
     85    // NOTE: This method is only called one place, NumberRangeFormatterImpl.
     86    // The call site only cares about != 0 and != 1.
     87    // Return a very large value so that if this method is used elsewhere, we should notice.
     88    return 999;
     89 }
     90 
     91 bool ScientificModifier::isStrong() const {
     92    // Scientific is always strong
     93    return true;
     94 }
     95 
     96 bool ScientificModifier::containsField(Field field) const {
     97    (void)field;
     98    // This method is not used for inner modifiers.
     99    UPRV_UNREACHABLE_EXIT;
    100 }
    101 
    102 void ScientificModifier::getParameters(Parameters& output) const {
    103    // Not part of any plural sets
    104    output.obj = nullptr;
    105 }
    106 
    107 bool ScientificModifier::strictEquals(const Modifier& other) const {
    108    const auto* _other = dynamic_cast<const ScientificModifier*>(&other);
    109    if (_other == nullptr) {
    110        return false;
    111    }
    112    // TODO: Check for locale symbols and settings as well? Could be less efficient.
    113    return fExponent == _other->fExponent;
    114 }
    115 
    116 // Note: Visual Studio does not compile this function without full name space. Why?
    117 icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
    118 const MicroPropsGenerator *parent) : 
    119 fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
    120 
    121 void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
    122                                        UErrorCode &status) const {
    123    fParent->processQuantity(quantity, micros, status);
    124    if (U_FAILURE(status)) { return; }
    125 
    126    // Do not apply scientific notation to special doubles
    127    if (quantity.isInfinite() || quantity.isNaN()) {
    128        micros.modInner = &micros.helpers.emptyStrongModifier;
    129        return;
    130    }
    131 
    132    // Treat zero as if it had magnitude 0
    133    int32_t exponent;
    134    if (quantity.isZeroish()) {
    135        if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
    136            // Show "00.000E0" on pattern "00.000E0"
    137            micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
    138            exponent = 0;
    139        } else {
    140            micros.rounder.apply(quantity, status);
    141            exponent = 0;
    142        }
    143    } else {
    144        exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);
    145    }
    146 
    147    // Use MicroProps's helper ScientificModifier and save it as the modInner.
    148    ScientificModifier &mod = micros.helpers.scientificModifier;
    149    mod.set(exponent, this);
    150    micros.modInner = &mod;
    151 
    152    // Change the exponent only after we select appropriate plural form
    153    // for formatting purposes so that we preserve expected formatted
    154    // string behavior.
    155    quantity.adjustExponent(exponent);
    156 
    157    // We already performed rounding. Do not perform it again.
    158    micros.rounder = RoundingImpl::passThrough();
    159 }
    160 
    161 int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
    162    int32_t interval = fSettings.fEngineeringInterval;
    163    int32_t digitsShown;
    164    if (fSettings.fRequireMinInt) {
    165        // For patterns like "000.00E0" and ".00E0"
    166        digitsShown = interval;
    167    } else if (interval <= 1) {
    168        // For patterns like "0.00E0" and "@@@E0"
    169        digitsShown = 1;
    170    } else {
    171        // For patterns like "##0.00"
    172        digitsShown = ((magnitude % interval + interval) % interval) + 1;
    173    }
    174    return digitsShown - magnitude - 1;
    175 }
    176 
    177 #endif /* #if !UCONFIG_NO_FORMATTING */