123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // © 2017 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- #include "unicode/utypes.h"
- #if !UCONFIG_NO_FORMATTING
- #include <cstdlib>
- #include "number_scientific.h"
- #include "number_utils.h"
- #include "formatted_string_builder.h"
- #include "unicode/unum.h"
- #include "number_microprops.h"
- using namespace icu;
- using namespace icu::number;
- using namespace icu::number::impl;
- // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
- //
- // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
- //
- // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
- // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
- // instances. This scheme reduces the number of object creations by 1 in both safe and unsafe.
- //
- // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
- // the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
- ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
- void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) {
- // ScientificModifier should be set only once.
- U_ASSERT(fHandler == nullptr);
- fExponent = exponent;
- fHandler = handler;
- }
- int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
- UErrorCode &status) const {
- // FIXME: Localized exponent separator location.
- int i = rightIndex;
- // Append the exponent separator and sign
- i += output.insert(
- i,
- fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
- {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD},
- status);
- if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) {
- i += output.insert(
- i,
- fHandler->fSymbols
- ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),
- {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
- status);
- } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) {
- i += output.insert(
- i,
- fHandler->fSymbols
- ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),
- {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
- status);
- }
- // Append the exponent digits (using a simple inline algorithm)
- int32_t disp = std::abs(fExponent);
- for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
- auto d = static_cast<int8_t>(disp % 10);
- i += utils::insertDigitFromSymbols(
- output,
- i - j,
- d,
- *fHandler->fSymbols,
- {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD},
- status);
- }
- return i - rightIndex;
- }
- int32_t ScientificModifier::getPrefixLength() const {
- // TODO: Localized exponent separator location.
- return 0;
- }
- int32_t ScientificModifier::getCodePointCount() const {
- // NOTE: This method is only called one place, NumberRangeFormatterImpl.
- // The call site only cares about != 0 and != 1.
- // Return a very large value so that if this method is used elsewhere, we should notice.
- return 999;
- }
- bool ScientificModifier::isStrong() const {
- // Scientific is always strong
- return true;
- }
- bool ScientificModifier::containsField(Field field) const {
- (void)field;
- // This method is not used for inner modifiers.
- UPRV_UNREACHABLE_EXIT;
- }
- void ScientificModifier::getParameters(Parameters& output) const {
- // Not part of any plural sets
- output.obj = nullptr;
- }
- bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const {
- auto* _other = dynamic_cast<const ScientificModifier*>(&other);
- if (_other == nullptr) {
- return false;
- }
- // TODO: Check for locale symbols and settings as well? Could be less efficient.
- return fExponent == _other->fExponent;
- }
- // Note: Visual Studio does not compile this function without full name space. Why?
- icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
- const MicroPropsGenerator *parent) :
- fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
- void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,
- UErrorCode &status) const {
- fParent->processQuantity(quantity, micros, status);
- if (U_FAILURE(status)) { return; }
- // Do not apply scientific notation to special doubles
- if (quantity.isInfinite() || quantity.isNaN()) {
- micros.modInner = µs.helpers.emptyStrongModifier;
- return;
- }
- // Treat zero as if it had magnitude 0
- int32_t exponent;
- if (quantity.isZeroish()) {
- if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
- // Show "00.000E0" on pattern "00.000E0"
- micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
- exponent = 0;
- } else {
- micros.rounder.apply(quantity, status);
- exponent = 0;
- }
- } else {
- exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);
- }
- // Use MicroProps's helper ScientificModifier and save it as the modInner.
- ScientificModifier &mod = micros.helpers.scientificModifier;
- mod.set(exponent, this);
- micros.modInner = &mod;
- // Change the exponent only after we select appropriate plural form
- // for formatting purposes so that we preserve expected formatted
- // string behavior.
- quantity.adjustExponent(exponent);
- // We already performed rounding. Do not perform it again.
- micros.rounder = RoundingImpl::passThrough();
- }
- int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
- int32_t interval = fSettings.fEngineeringInterval;
- int32_t digitsShown;
- if (fSettings.fRequireMinInt) {
- // For patterns like "000.00E0" and ".00E0"
- digitsShown = interval;
- } else if (interval <= 1) {
- // For patterns like "0.00E0" and "@@@E0"
- digitsShown = 1;
- } else {
- // For patterns like "##0.00"
- digitsShown = ((magnitude % interval + interval) % interval) + 1;
- }
- return digitsShown - magnitude - 1;
- }
- #endif /* #if !UCONFIG_NO_FORMATTING */
|