/src/icu/source/i18n/number_scientific.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
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  |  |  | 
15  |  | using namespace icu;  | 
16  |  | using namespace icu::number;  | 
17  |  | using 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  |  |  | 
30  | 0  | ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {} | 
31  |  |  | 
32  | 0  | void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) { | 
33  |  |     // ScientificModifier should be set only once.  | 
34  | 0  |     U_ASSERT(fHandler == nullptr);  | 
35  | 0  |     fExponent = exponent;  | 
36  | 0  |     fHandler = handler;  | 
37  | 0  | }  | 
38  |  |  | 
39  |  | int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,  | 
40  | 0  |                                   UErrorCode &status) const { | 
41  |  |     // FIXME: Localized exponent separator location.  | 
42  | 0  |     int i = rightIndex;  | 
43  |  |     // Append the exponent separator and sign  | 
44  | 0  |     i += output.insert(  | 
45  | 0  |             i,  | 
46  | 0  |             fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),  | 
47  | 0  |             {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD}, | 
48  | 0  |             status);  | 
49  | 0  |     if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) { | 
50  | 0  |         i += output.insert(  | 
51  | 0  |                 i,  | 
52  | 0  |                 fHandler->fSymbols  | 
53  | 0  |                         ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),  | 
54  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, | 
55  | 0  |                 status);  | 
56  | 0  |     } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) { | 
57  | 0  |         i += output.insert(  | 
58  | 0  |                 i,  | 
59  | 0  |                 fHandler->fSymbols  | 
60  | 0  |                         ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),  | 
61  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD}, | 
62  | 0  |                 status);  | 
63  | 0  |     }  | 
64  |  |     // Append the exponent digits (using a simple inline algorithm)  | 
65  | 0  |     int32_t disp = std::abs(fExponent);  | 
66  | 0  |     for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) { | 
67  | 0  |         auto d = static_cast<int8_t>(disp % 10);  | 
68  | 0  |         i += utils::insertDigitFromSymbols(  | 
69  | 0  |                 output,  | 
70  | 0  |                 i - j,  | 
71  | 0  |                 d,  | 
72  | 0  |                 *fHandler->fSymbols,  | 
73  | 0  |                 {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD}, | 
74  | 0  |                 status);  | 
75  | 0  |     }  | 
76  | 0  |     return i - rightIndex;  | 
77  | 0  | }  | 
78  |  |  | 
79  | 0  | int32_t ScientificModifier::getPrefixLength() const { | 
80  |  |     // TODO: Localized exponent separator location.  | 
81  | 0  |     return 0;  | 
82  | 0  | }  | 
83  |  |  | 
84  | 0  | 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.  | 
88  | 0  |     return 999;  | 
89  | 0  | }  | 
90  |  |  | 
91  | 0  | bool ScientificModifier::isStrong() const { | 
92  |  |     // Scientific is always strong  | 
93  | 0  |     return true;  | 
94  | 0  | }  | 
95  |  |  | 
96  | 0  | bool ScientificModifier::containsField(Field field) const { | 
97  | 0  |     (void)field;  | 
98  |  |     // This method is not used for inner modifiers.  | 
99  | 0  |     UPRV_UNREACHABLE;  | 
100  | 0  | }  | 
101  |  |  | 
102  | 0  | void ScientificModifier::getParameters(Parameters& output) const { | 
103  |  |     // Not part of any plural sets  | 
104  | 0  |     output.obj = nullptr;  | 
105  | 0  | }  | 
106  |  |  | 
107  | 0  | bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const { | 
108  | 0  |     auto* _other = dynamic_cast<const ScientificModifier*>(&other);  | 
109  | 0  |     if (_other == nullptr) { | 
110  | 0  |         return false;  | 
111  | 0  |     }  | 
112  |  |     // TODO: Check for locale symbols and settings as well? Could be less efficient.  | 
113  | 0  |     return fExponent == _other->fExponent;  | 
114  | 0  | }  | 
115  |  |  | 
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  | 0  |   fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {} | 
120  |  |  | 
121  |  | void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,  | 
122  | 0  |                                         UErrorCode &status) const { | 
123  | 0  |     fParent->processQuantity(quantity, micros, status);  | 
124  | 0  |     if (U_FAILURE(status)) { return; } | 
125  |  |  | 
126  |  |     // Do not apply scientific notation to special doubles  | 
127  | 0  |     if (quantity.isInfinite() || quantity.isNaN()) { | 
128  | 0  |         micros.modInner = µs.helpers.emptyStrongModifier;  | 
129  | 0  |         return;  | 
130  | 0  |     }  | 
131  |  |  | 
132  |  |     // Treat zero as if it had magnitude 0  | 
133  | 0  |     int32_t exponent;  | 
134  | 0  |     if (quantity.isZeroish()) { | 
135  | 0  |         if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) { | 
136  |  |             // Show "00.000E0" on pattern "00.000E0"  | 
137  | 0  |             micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);  | 
138  | 0  |             exponent = 0;  | 
139  | 0  |         } else { | 
140  | 0  |             micros.rounder.apply(quantity, status);  | 
141  | 0  |             exponent = 0;  | 
142  | 0  |         }  | 
143  | 0  |     } else { | 
144  | 0  |         exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);  | 
145  | 0  |     }  | 
146  |  |  | 
147  |  |     // Use MicroProps's helper ScientificModifier and save it as the modInner.  | 
148  | 0  |     ScientificModifier &mod = micros.helpers.scientificModifier;  | 
149  | 0  |     mod.set(exponent, this);  | 
150  | 0  |     micros.modInner = &mod;  | 
151  |  |  | 
152  |  |     // Change the exponent only after we select appropriate plural form  | 
153  |  |     // for formatting purposes so that we preserve expected formatted  | 
154  |  |     // string behavior.  | 
155  | 0  |     quantity.adjustExponent(exponent);  | 
156  |  |  | 
157  |  |     // We already performed rounding. Do not perform it again.  | 
158  | 0  |     micros.rounder = RoundingImpl::passThrough();  | 
159  | 0  | }  | 
160  |  |  | 
161  | 0  | int32_t ScientificHandler::getMultiplier(int32_t magnitude) const { | 
162  | 0  |     int32_t interval = fSettings.fEngineeringInterval;  | 
163  | 0  |     int32_t digitsShown;  | 
164  | 0  |     if (fSettings.fRequireMinInt) { | 
165  |  |         // For patterns like "000.00E0" and ".00E0"  | 
166  | 0  |         digitsShown = interval;  | 
167  | 0  |     } else if (interval <= 1) { | 
168  |  |         // For patterns like "0.00E0" and "@@@E0"  | 
169  | 0  |         digitsShown = 1;  | 
170  | 0  |     } else { | 
171  |  |         // For patterns like "##0.00"  | 
172  | 0  |         digitsShown = ((magnitude % interval + interval) % interval) + 1;  | 
173  | 0  |     }  | 
174  | 0  |     return digitsShown - magnitude - 1;  | 
175  | 0  | }  | 
176  |  |  | 
177  |  | #endif /* #if !UCONFIG_NO_FORMATTING */  |