1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
9 #include "number_scientific.h"
10 #include "number_utils.h"
11 #include "number_stringbuilder.h"
12 #include "unicode/unum.h"
13 #include "number_microprops.h"
16 using namespace icu::number
;
17 using namespace icu::number::impl
;
19 // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
21 // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
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.
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.
30 ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
32 void ScientificModifier::set(int32_t exponent
, const ScientificHandler
*handler
) {
33 // ScientificModifier should be set only once.
34 U_ASSERT(fHandler
== nullptr);
39 int32_t ScientificModifier::apply(NumberStringBuilder
&output
, int32_t /*leftIndex*/, int32_t rightIndex
,
40 UErrorCode
&status
) const {
41 // FIXME: Localized exponent separator location.
43 // Append the exponent separator and sign
46 fHandler
->fSymbols
->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol
),
47 UNUM_EXPONENT_SYMBOL_FIELD
,
49 if (fExponent
< 0 && fHandler
->fSettings
.fExponentSignDisplay
!= UNUM_SIGN_NEVER
) {
53 ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol
),
54 UNUM_EXPONENT_SIGN_FIELD
,
56 } else if (fExponent
>= 0 && fHandler
->fSettings
.fExponentSignDisplay
== UNUM_SIGN_ALWAYS
) {
60 ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol
),
61 UNUM_EXPONENT_SIGN_FIELD
,
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(
76 return i
- rightIndex
;
79 int32_t ScientificModifier::getPrefixLength() const {
80 // TODO: Localized exponent separator location.
84 int32_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.
91 bool ScientificModifier::isStrong() const {
92 // Scientific is always strong
96 bool ScientificModifier::containsField(UNumberFormatFields field
) const {
98 // This method is not used for inner modifiers.
102 void ScientificModifier::getParameters(Parameters
& output
) const {
103 // Not part of any plural sets
104 output
.obj
= nullptr;
107 bool ScientificModifier::semanticallyEquivalent(const Modifier
& other
) const {
108 auto* _other
= dynamic_cast<const ScientificModifier
*>(&other
);
109 if (_other
== nullptr) {
112 // TODO: Check for locale symbols and settings as well? Could be less efficient.
113 return fExponent
== _other
->fExponent
;
116 // Note: Visual Studio does not compile this function without full name space. Why?
117 icu::number::impl::ScientificHandler::ScientificHandler(const Notation
*notation
, const DecimalFormatSymbols
*symbols
,
118 const MicroPropsGenerator
*parent
) :
119 fSettings(notation
->fUnion
.scientific
), fSymbols(symbols
), fParent(parent
) {}
121 void ScientificHandler::processQuantity(DecimalQuantity
&quantity
, MicroProps
µs
,
122 UErrorCode
&status
) const {
123 fParent
->processQuantity(quantity
, micros
, status
);
124 if (U_FAILURE(status
)) { return; }
126 // Treat zero as if it had magnitude 0
128 if (quantity
.isZero()) {
129 if (fSettings
.fRequireMinInt
&& micros
.rounder
.isSignificantDigits()) {
130 // Show "00.000E0" on pattern "00.000E0"
131 micros
.rounder
.apply(quantity
, fSettings
.fEngineeringInterval
, status
);
134 micros
.rounder
.apply(quantity
, status
);
138 exponent
= -micros
.rounder
.chooseMultiplierAndApply(quantity
, *this, status
);
141 // Use MicroProps's helper ScientificModifier and save it as the modInner.
142 ScientificModifier
&mod
= micros
.helpers
.scientificModifier
;
143 mod
.set(exponent
, this);
144 micros
.modInner
= &mod
;
146 // We already performed rounding. Do not perform it again.
147 micros
.rounder
= RoundingImpl::passThrough();
150 int32_t ScientificHandler::getMultiplier(int32_t magnitude
) const {
151 int32_t interval
= fSettings
.fEngineeringInterval
;
153 if (fSettings
.fRequireMinInt
) {
154 // For patterns like "000.00E0" and ".00E0"
155 digitsShown
= interval
;
156 } else if (interval
<= 1) {
157 // For patterns like "0.00E0" and "@@@E0"
160 // For patterns like "##0.00"
161 digitsShown
= ((magnitude
% interval
+ interval
) % interval
) + 1;
163 return digitsShown
- magnitude
- 1;
166 #endif /* #if !UCONFIG_NO_FORMATTING */