• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ScientificModifier()29 ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
30 
set(int32_t exponent,const ScientificHandler * handler)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 
apply(NumberStringBuilder & output,int32_t,int32_t rightIndex,UErrorCode & status) const38 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 
getPrefixLength(UErrorCode & status) const73 int32_t ScientificModifier::getPrefixLength(UErrorCode &status) const {
74     (void)status;
75     // TODO: Localized exponent separator location.
76     return 0;
77 }
78 
getCodePointCount(UErrorCode & status) const79 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 
isStrong() const86 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?
ScientificHandler(const Notation * notation,const DecimalFormatSymbols * symbols,const MicroPropsGenerator * parent)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 
processQuantity(DecimalQuantity & quantity,MicroProps & micros,UErrorCode & status) const96 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 
getMultiplier(int32_t magnitude) const122 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