Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/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 "number_stringbuilder.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
0
    // 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(NumberStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
40
0
                                  UErrorCode &status) const {
41
0
    // FIXME: Localized exponent separator location.
42
0
    int i = rightIndex;
43
0
    // Append the exponent separator and sign
44
0
    i += output.insert(
45
0
            i,
46
0
            fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
47
0
            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
                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
                UNUM_EXPONENT_SIGN_FIELD,
62
0
                status);
63
0
    }
64
0
    // 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
                UNUM_EXPONENT_FIELD,
74
0
                status);
75
0
    }
76
0
    return i - rightIndex;
77
0
}
78
79
0
int32_t ScientificModifier::getPrefixLength(UErrorCode &status) const {
80
0
    (void)status;
81
0
    // TODO: Localized exponent separator location.
82
0
    return 0;
83
0
}
84
85
0
int32_t ScientificModifier::getCodePointCount(UErrorCode &status) const {
86
0
    (void)status;
87
0
    // This method is not used for strong modifiers.
88
0
    U_ASSERT(false);
89
0
    return 0;
90
0
}
91
92
0
bool ScientificModifier::isStrong() const {
93
0
    // Scientific is always strong
94
0
    return true;
95
0
}
96
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
0
  fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
101
102
void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
103
0
                                        UErrorCode &status) const {
104
0
    fParent->processQuantity(quantity, micros, status);
105
0
    if (U_FAILURE(status)) { return; }
106
0
107
0
    // Treat zero as if it had magnitude 0
108
0
    int32_t exponent;
109
0
    if (quantity.isZero()) {
110
0
        if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
111
0
            // Show "00.000E0" on pattern "00.000E0"
112
0
            micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
113
0
            exponent = 0;
114
0
        } else {
115
0
            micros.rounder.apply(quantity, status);
116
0
            exponent = 0;
117
0
        }
118
0
    } else {
119
0
        exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);
120
0
    }
121
0
122
0
    // Use MicroProps's helper ScientificModifier and save it as the modInner.
123
0
    ScientificModifier &mod = micros.helpers.scientificModifier;
124
0
    mod.set(exponent, this);
125
0
    micros.modInner = &mod;
126
0
127
0
    // We already performed rounding. Do not perform it again.
128
0
    micros.rounder = RoundingImpl::passThrough();
129
0
}
130
131
0
int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
132
0
    int32_t interval = fSettings.fEngineeringInterval;
133
0
    int32_t digitsShown;
134
0
    if (fSettings.fRequireMinInt) {
135
0
        // For patterns like "000.00E0" and ".00E0"
136
0
        digitsShown = interval;
137
0
    } else if (interval <= 1) {
138
0
        // For patterns like "0.00E0" and "@@@E0"
139
0
        digitsShown = 1;
140
0
    } else {
141
0
        // For patterns like "##0.00"
142
0
        digitsShown = ((magnitude % interval + interval) % interval) + 1;
143
0
    }
144
0
    return digitsShown - magnitude - 1;
145
0
}
146
147
#endif /* #if !UCONFIG_NO_FORMATTING */