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
getRoundingDirection(bool isEven,bool isNegative,Section section,RoundingMode roundingMode,UErrorCode & status)45 getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
46                      UErrorCode &status) {
47     switch (roundingMode) {
48         case RoundingMode::UNUM_ROUND_UP:
49             // round away from zero
50             return false;
51 
52         case RoundingMode::UNUM_ROUND_DOWN:
53             // round toward zero
54             return true;
55 
56         case RoundingMode::UNUM_ROUND_CEILING:
57             // round toward positive infinity
58             return isNegative;
59 
60         case RoundingMode::UNUM_ROUND_FLOOR:
61             // round toward negative infinity
62             return !isNegative;
63 
64         case RoundingMode::UNUM_ROUND_HALFUP:
65             switch (section) {
66                 case SECTION_MIDPOINT:
67                     return false;
68                 case SECTION_LOWER:
69                     return true;
70                 case SECTION_UPPER:
71                     return false;
72                 default:
73                     break;
74             }
75             break;
76 
77         case RoundingMode::UNUM_ROUND_HALFDOWN:
78             switch (section) {
79                 case SECTION_MIDPOINT:
80                     return true;
81                 case SECTION_LOWER:
82                     return true;
83                 case SECTION_UPPER:
84                     return false;
85                 default:
86                     break;
87             }
88             break;
89 
90         case RoundingMode::UNUM_ROUND_HALFEVEN:
91             switch (section) {
92                 case SECTION_MIDPOINT:
93                     return isEven;
94                 case SECTION_LOWER:
95                     return true;
96                 case SECTION_UPPER:
97                     return false;
98                 default:
99                     break;
100             }
101             break;
102 
103         default:
104             break;
105     }
106 
107     status = U_FORMAT_INEXACT_ERROR;
108     return false;
109 }
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  */
roundsAtMidpoint(int roundingMode)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  * @param singleDigit An output parameter; set to a number if that is the
139  *        only digit in the double, or -1 if there is more than one digit.
140  */
141 digits_t doubleFractionLength(double input, int8_t* singleDigit);
142 
143 } // namespace roundingutils
144 
145 
146 /**
147  * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
148  *
149  * This class does not exist in Java: instead, the base Precision class is used.
150  */
151 class RoundingImpl {
152   public:
153     RoundingImpl() = default;  // default constructor: leaves object in undefined state
154 
155     RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
156                  const CurrencyUnit& currency, UErrorCode& status);
157 
158     static RoundingImpl passThrough();
159 
160     /** Required for ScientificFormatter */
161     bool isSignificantDigits() const;
162 
163     /**
164      * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
165      * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
166      *
167      * <p>
168      * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
169      * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
170      * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
171      * change your multiplier to be -6, and you get 1.0E6, which is correct.
172      *
173      * @param input The quantity to process.
174      * @param producer Function to call to return a multiplier based on a magnitude.
175      * @return The number of orders of magnitude the input was adjusted by this method.
176      */
177     int32_t
178     chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
179                              UErrorCode &status);
180 
181     void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
182 
183     /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
184     void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
185 
186   private:
187     Precision fPrecision;
188     UNumberFormatRoundingMode fRoundingMode;
189     bool fPassThrough;
190 };
191 
192 
193 } // namespace impl
194 } // namespace number
195 U_NAMESPACE_END
196 
197 #endif //__NUMBER_ROUNDINGUTILS_H__
198 
199 #endif /* #if !UCONFIG_NO_FORMATTING */
200