Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/number_roundingutils.h
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
#ifndef __NUMBER_ROUNDINGUTILS_H__
8
#define __NUMBER_ROUNDINGUTILS_H__
9
10
#include "number_types.h"
11
12
U_NAMESPACE_BEGIN
13
namespace number {
14
namespace impl {
15
namespace roundingutils {
16
17
enum Section {
18
    SECTION_LOWER_EDGE = -1,
19
    SECTION_UPPER_EDGE = -2,
20
    SECTION_LOWER = 1,
21
    SECTION_MIDPOINT = 2,
22
    SECTION_UPPER = 3
23
};
24
25
/**
26
 * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining
27
 * whether the value should be rounded toward infinity or toward zero.
28
 *
29
 * <p>The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK
30
 * showed that ints were demonstrably faster than enums in switch statements.
31
 *
32
 * @param isEven Whether the digit immediately before the rounding magnitude is even.
33
 * @param isNegative Whether the quantity is negative.
34
 * @param section Whether the part of the quantity to the right of the rounding magnitude is
35
 *     exactly halfway between two digits, whether it is in the lower part (closer to zero), or
36
 *     whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link
37
 *     #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}.
38
 * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via
39
 *     {@link RoundingMode#ordinal}.
40
 * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary.
41
 * @return true if the number should be rounded toward zero; false if it should be rounded toward
42
 *     infinity.
43
 */
44
inline bool
45
getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
46
0
                     UErrorCode &status) {
47
0
    switch (roundingMode) {
48
0
        case RoundingMode::UNUM_ROUND_UP:
49
0
            // round away from zero
50
0
            return false;
51
0
52
0
        case RoundingMode::UNUM_ROUND_DOWN:
53
0
            // round toward zero
54
0
            return true;
55
0
56
0
        case RoundingMode::UNUM_ROUND_CEILING:
57
0
            // round toward positive infinity
58
0
            return isNegative;
59
0
60
0
        case RoundingMode::UNUM_ROUND_FLOOR:
61
0
            // round toward negative infinity
62
0
            return !isNegative;
63
0
64
0
        case RoundingMode::UNUM_ROUND_HALFUP:
65
0
            switch (section) {
66
0
                case SECTION_MIDPOINT:
67
0
                    return false;
68
0
                case SECTION_LOWER:
69
0
                    return true;
70
0
                case SECTION_UPPER:
71
0
                    return false;
72
0
                default:
73
0
                    break;
74
0
            }
75
0
            break;
76
0
77
0
        case RoundingMode::UNUM_ROUND_HALFDOWN:
78
0
            switch (section) {
79
0
                case SECTION_MIDPOINT:
80
0
                    return true;
81
0
                case SECTION_LOWER:
82
0
                    return true;
83
0
                case SECTION_UPPER:
84
0
                    return false;
85
0
                default:
86
0
                    break;
87
0
            }
88
0
            break;
89
0
90
0
        case RoundingMode::UNUM_ROUND_HALFEVEN:
91
0
            switch (section) {
92
0
                case SECTION_MIDPOINT:
93
0
                    return isEven;
94
0
                case SECTION_LOWER:
95
0
                    return true;
96
0
                case SECTION_UPPER:
97
0
                    return false;
98
0
                default:
99
0
                    break;
100
0
            }
101
0
            break;
102
0
103
0
        default:
104
0
            break;
105
0
    }
106
0
107
0
    status = U_FORMAT_INEXACT_ERROR;
108
0
    return false;
109
0
}
110
111
/**
112
 * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding
113
 * boundary is the point at which a number switches from being rounded down to being rounded up.
114
 * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at
115
 * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR,
116
 * the rounding boundary is at the "edge", and this function would return false.
117
 *
118
 * @param roundingMode The integer version of the {@link RoundingMode}.
119
 * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise.
120
 */
121
inline bool roundsAtMidpoint(int roundingMode) {
122
    switch (roundingMode) {
123
        case RoundingMode::UNUM_ROUND_UP:
124
        case RoundingMode::UNUM_ROUND_DOWN:
125
        case RoundingMode::UNUM_ROUND_CEILING:
126
        case RoundingMode::UNUM_ROUND_FLOOR:
127
            return false;
128
129
        default:
130
            return true;
131
    }
132
}
133
134
/**
135
 * Computes the number of fraction digits in a double. Used for computing maxFrac for an increment.
136
 * Calls into the DoubleToStringConverter library to do so.
137
 */
138
digits_t doubleFractionLength(double input);
139
140
} // namespace roundingutils
141
142
143
/**
144
 * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
145
 *
146
 * This class does not exist in Java: instead, the base Precision class is used.
147
 */
148
class RoundingImpl {
149
  public:
150
0
    RoundingImpl() = default;  // default constructor: leaves object in undefined state
151
152
    RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
153
                 const CurrencyUnit& currency, UErrorCode& status);
154
155
    static RoundingImpl passThrough();
156
157
    /** Required for ScientificFormatter */
158
    bool isSignificantDigits() const;
159
160
    /**
161
     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
162
     * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
163
     *
164
     * <p>
165
     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
166
     * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
167
     * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
168
     * change your multiplier to be -6, and you get 1.0E6, which is correct.
169
     *
170
     * @param input The quantity to process.
171
     * @param producer Function to call to return a multiplier based on a magnitude.
172
     * @return The number of orders of magnitude the input was adjusted by this method.
173
     */
174
    int32_t
175
    chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
176
                             UErrorCode &status);
177
178
    void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
179
180
    /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
181
    void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
182
183
  private:
184
    Precision fPrecision;
185
    UNumberFormatRoundingMode fRoundingMode;
186
    bool fPassThrough;
187
};
188
189
190
} // namespace impl
191
} // namespace number
192
U_NAMESPACE_END
193
194
#endif //__NUMBER_ROUNDINGUTILS_H__
195
196
#endif /* #if !UCONFIG_NO_FORMATTING */