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
15using namespace icu;
16using namespace icu::number;
17using 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
30ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
31
32void 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
39int32_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 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 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 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 UNUM_EXPONENT_FIELD,
74 status);
75 }
76 return i - rightIndex;
77}
78
79int32_t ScientificModifier::getPrefixLength() const {
80 // TODO: Localized exponent separator location.
81 return 0;
82}
83
84int32_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
91bool ScientificModifier::isStrong() const {
92 // Scientific is always strong
93 return true;
94}
95
96bool ScientificModifier::containsField(UNumberFormatFields field) const {
97 (void)field;
98 // This method is not used for inner modifiers.
99 UPRV_UNREACHABLE;
100}
101
102void ScientificModifier::getParameters(Parameters& output) const {
103 // Not part of any plural sets
104 output.obj = nullptr;
105}
106
107bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const {
108 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?
117icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
118 const MicroPropsGenerator *parent) :
119 fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
120
121void 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 // We already performed rounding. Do not perform it again.
153 micros.rounder = RoundingImpl::passThrough();
154}
155
156int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
157 int32_t interval = fSettings.fEngineeringInterval;
158 int32_t digitsShown;
159 if (fSettings.fRequireMinInt) {
160 // For patterns like "000.00E0" and ".00E0"
161 digitsShown = interval;
162 } else if (interval <= 1) {
163 // For patterns like "0.00E0" and "@@@E0"
164 digitsShown = 1;
165 } else {
166 // For patterns like "##0.00"
167 digitsShown = ((magnitude % interval + interval) % interval) + 1;
168 }
169 return digitsShown - magnitude - 1;
170}
171
172#endif /* #if !UCONFIG_NO_FORMATTING */
173