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 <cstdlib>
      9 #include "number_scientific.h"
     10 #include "number_utils.h"
     11 #include "number_stringbuilder.h"
     12 #include "unicode/unum.h"
     13 
     14 using namespace icu;
     15 using namespace icu::number;
     16 using namespace icu::number::impl;
     17 
     18 // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
     19 //
     20 // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
     21 //
     22 // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
     23 // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
     24 // instances.  This scheme reduces the number of object creations by 1 in both safe and unsafe.
     25 //
     26 // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
     27 // the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
     28 
     29 ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
     30 
     31 void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) {
     32     // ScientificModifier should be set only once.
     33     U_ASSERT(fHandler == nullptr);
     34     fExponent = exponent;
     35     fHandler = handler;
     36 }
     37 
     38 int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
     39                                   UErrorCode &status) const {
     40     // FIXME: Localized exponent separator location.
     41     int i = rightIndex;
     42     // Append the exponent separator and sign
     43     i += output.insert(
     44             i,
     45             fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
     46             UNUM_EXPONENT_SYMBOL_FIELD,
     47             status);
     48     if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) {
     49         i += output.insert(
     50                 i,
     51                 fHandler->fSymbols
     52                         ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),
     53                 UNUM_EXPONENT_SIGN_FIELD,
     54                 status);
     55     } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) {
     56         i += output.insert(
     57                 i,
     58                 fHandler->fSymbols
     59                         ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),
     60                 UNUM_EXPONENT_SIGN_FIELD,
     61                 status);
     62     }
     63     // Append the exponent digits (using a simple inline algorithm)
     64     int32_t disp = std::abs(fExponent);
     65     for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
     66         auto d = static_cast<int8_t>(disp % 10);
     67         const UnicodeString &digitString = getDigitFromSymbols(d, *fHandler->fSymbols);
     68         i += output.insert(i - j, digitString, UNUM_EXPONENT_FIELD, status);
     69     }
     70     return i - rightIndex;
     71 }
     72 
     73 int32_t ScientificModifier::getPrefixLength(UErrorCode &status) const {
     74     (void)status;
     75     // TODO: Localized exponent separator location.
     76     return 0;
     77 }
     78 
     79 int32_t ScientificModifier::getCodePointCount(UErrorCode &status) const {
     80     (void)status;
     81     // This method is not used for strong modifiers.
     82     U_ASSERT(false);
     83     return 0;
     84 }
     85 
     86 bool ScientificModifier::isStrong() const {
     87     // Scientific is always strong
     88     return true;
     89 }
     90 
     91 // Note: Visual Studio does not compile this function without full name space. Why?
     92 icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
     93 	const MicroPropsGenerator *parent) :
     94 	fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
     95 
     96 void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
     97                                         UErrorCode &status) const {
     98     fParent->processQuantity(quantity, micros, status);
     99     if (U_FAILURE(status)) { return; }
    100 
    101     // Treat zero as if it had magnitude 0
    102     int32_t exponent;
    103     if (quantity.isZero()) {
    104         if (fSettings.fRequireMinInt && micros.rounding.fType == Rounder::RND_SIGNIFICANT) {
    105             // Show "00.000E0" on pattern "00.000E0"
    106             micros.rounding.apply(quantity, fSettings.fEngineeringInterval, status);
    107             exponent = 0;
    108         } else {
    109             micros.rounding.apply(quantity, status);
    110             exponent = 0;
    111         }
    112     } else {
    113         exponent = -micros.rounding.chooseMultiplierAndApply(quantity, *this, status);
    114     }
    115 
    116     // Use MicroProps's helper ScientificModifier and save it as the modInner.
    117     ScientificModifier &mod = micros.helpers.scientificModifier;
    118     mod.set(exponent, this);
    119     micros.modInner = &mod;
    120 }
    121 
    122 int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
    123     int32_t interval = fSettings.fEngineeringInterval;
    124     int32_t digitsShown;
    125     if (fSettings.fRequireMinInt) {
    126         // For patterns like "000.00E0" and ".00E0"
    127         digitsShown = interval;
    128     } else if (interval <= 1) {
    129         // For patterns like "0.00E0" and "@@@E0"
    130         digitsShown = 1;
    131     } else {
    132         // For patterns like "##0.00"
    133         digitsShown = ((magnitude % interval + interval) % interval) + 1;
    134     }
    135     return digitsShown - magnitude - 1;
    136 }
    137 
    138 #endif /* #if !UCONFIG_NO_FORMATTING */
    139