1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include <_foundation_unicode/utypes.h>
#if !UCONFIG_NO_FORMATTING
#include <cstdlib>
#include "number_scientific.h"
#include "number_utils.h"
#include "formatted_string_builder.h"
#include <_foundation_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 */
|