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