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(UErrorCode
&status
) const {
81 // TODO: Localized exponent separator location.
85 int32_t ScientificModifier::getCodePointCount(UErrorCode
&status
) const {
87 // This method is not used for strong modifiers.
92 bool ScientificModifier::isStrong() const {
93 // Scientific is always strong
97 // Note: Visual Studio does not compile this function without full name space. Why?
98 icu::number::impl::ScientificHandler::ScientificHandler(const Notation
*notation
, const DecimalFormatSymbols
*symbols
,
99 const MicroPropsGenerator
*parent
) :
100 fSettings(notation
->fUnion
.scientific
), fSymbols(symbols
), fParent(parent
) {}
102 void ScientificHandler::processQuantity(DecimalQuantity
&quantity
, MicroProps
µs
,
103 UErrorCode
&status
) const {
104 fParent
->processQuantity(quantity
, micros
, status
);
105 if (U_FAILURE(status
)) { return; }
107 // Treat zero as if it had magnitude 0
109 if (quantity
.isZero()) {
110 if (fSettings
.fRequireMinInt
&& micros
.rounder
.isSignificantDigits()) {
111 // Show "00.000E0" on pattern "00.000E0"
112 micros
.rounder
.apply(quantity
, fSettings
.fEngineeringInterval
, status
);
115 micros
.rounder
.apply(quantity
, status
);
119 exponent
= -micros
.rounder
.chooseMultiplierAndApply(quantity
, *this, status
);
122 // Use MicroProps's helper ScientificModifier and save it as the modInner.
123 ScientificModifier
&mod
= micros
.helpers
.scientificModifier
;
124 mod
.set(exponent
, this);
125 micros
.modInner
= &mod
;
127 // We already performed rounding. Do not perform it again.
128 micros
.rounder
= RoundingImpl::passThrough();
131 int32_t ScientificHandler::getMultiplier(int32_t magnitude
) const {
132 int32_t interval
= fSettings
.fEngineeringInterval
;
134 if (fSettings
.fRequireMinInt
) {
135 // For patterns like "000.00E0" and ".00E0"
136 digitsShown
= interval
;
137 } else if (interval
<= 1) {
138 // For patterns like "0.00E0" and "@@@E0"
141 // For patterns like "##0.00"
142 digitsShown
= ((magnitude
% interval
+ interval
) % interval
) + 1;
144 return digitsShown
- magnitude
- 1;
147 #endif /* #if !UCONFIG_NO_FORMATTING */