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 µs, 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