LEFT | RIGHT |
(no file at all) | |
1 // © 2017 and later: Unicode, Inc. and others. | 1 // © 2017 and later: Unicode, Inc. and others. |
2 // License & terms of use: http://www.unicode.org/copyright.html#License | 2 // License & terms of use: http://www.unicode.org/copyright.html#License |
3 package com.ibm.icu.impl.number; | 3 package com.ibm.icu.impl.number; |
4 | 4 |
5 import java.math.BigDecimal; | 5 import java.math.BigDecimal; |
6 import java.math.MathContext; | 6 import java.math.MathContext; |
7 import java.math.RoundingMode; | 7 import java.math.RoundingMode; |
8 | 8 |
9 /** @author sffc */ | 9 /** @author sffc */ |
10 public class RoundingUtils { | 10 public class RoundingUtils { |
11 | 11 |
12 public static final int SECTION_LOWER = 1; | 12 public static final int SECTION_LOWER = 1; |
13 public static final int SECTION_MIDPOINT = 2; | 13 public static final int SECTION_MIDPOINT = 2; |
14 public static final int SECTION_UPPER = 3; | 14 public static final int SECTION_UPPER = 3; |
15 | 15 |
16 /** | 16 /** |
17 * The default rounding mode. | 17 * The default rounding mode. |
18 */ | 18 */ |
19 public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_EVE
N; | 19 public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_E
VEN; |
20 | 20 |
21 /** | 21 /** |
22 * The maximum number of fraction places, integer numerals, or significant dig
its. | 22 * The maximum number of fraction places, integer numerals, or significant d
igits. TODO: This does |
23 * TODO: This does not feel like the best home for this value. | 23 * not feel like the best home for this value. |
24 */ | 24 */ |
25 public static final int MAX_INT_FRAC_SIG = 100; | 25 public static final int MAX_INT_FRAC_SIG = 100; |
26 | 26 |
27 /** | 27 /** |
28 * Converts a rounding mode and metadata about the quantity being rounded to a
boolean determining | 28 * Converts a rounding mode and metadata about the quantity being rounded to
a boolean determining |
29 * whether the value should be rounded toward infinity or toward zero. | 29 * whether the value should be rounded toward infinity or toward zero. |
30 * | 30 * |
31 * <p>The parameters are of type int because benchmarks on an x86-64 processor
against OpenJDK | 31 * <p> |
32 * showed that ints were demonstrably faster than enums in switch statements. | 32 * The parameters are of type int because benchmarks on an x86-64 processor
against OpenJDK showed |
33 * | 33 * that ints were demonstrably faster than enums in switch statements. |
34 * @param isEven Whether the digit immediately before the rounding magnitude i
s even. | 34 * |
35 * @param isNegative Whether the quantity is negative. | 35 * @param isEven |
36 * @param section Whether the part of the quantity to the right of the roundin
g magnitude is | 36 * Whether the digit immediately before the rounding magnitude is
even. |
37 * exactly halfway between two digits, whether it is in the lower part (cl
oser to zero), or | 37 * @param isNegative |
38 * whether it is in the upper part (closer to infinity). See {@link #SECTI
ON_LOWER}, {@link | 38 * Whether the quantity is negative. |
39 * #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}. | 39 * @param section |
40 * @param roundingMode The integer version of the {@link RoundingMode}, which
you can get via | 40 * Whether the part of the quantity to the right of the rounding
magnitude is exactly |
41 * {@link RoundingMode#ordinal}. | 41 * halfway between two digits, whether it is in the lower part (c
loser to zero), or |
42 * @param reference A reference object to be used when throwing an ArithmeticE
xception. | 42 * whether it is in the upper part (closer to infinity). See {@li
nk #SECTION_LOWER}, |
43 * @return true if the number should be rounded toward zero; false if it shoul
d be rounded toward | 43 * {@link #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}. |
44 * infinity. | 44 * @param roundingMode |
45 */ | 45 * The integer version of the {@link RoundingMode}, which you can
get via |
46 public static boolean getRoundingDirection( | 46 * {@link RoundingMode#ordinal}. |
47 boolean isEven, boolean isNegative, int section, int roundingMode, Object
reference) { | 47 * @param reference |
48 switch (roundingMode) { | 48 * A reference object to be used when throwing an ArithmeticExcep
tion. |
49 case BigDecimal.ROUND_UP: | 49 * @return true if the number should be rounded toward zero; false if it sho
uld be rounded toward |
50 // round away from zero | 50 * infinity. |
51 return false; | 51 */ |
52 | 52 public static boolean getRoundingDirection( |
53 case BigDecimal.ROUND_DOWN: | 53 boolean isEven, |
54 // round toward zero | 54 boolean isNegative, |
55 return true; | 55 int section, |
56 | 56 int roundingMode, |
57 case BigDecimal.ROUND_CEILING: | 57 Object reference) { |
58 // round toward positive infinity | 58 switch (roundingMode) { |
59 return isNegative; | 59 case BigDecimal.ROUND_UP: |
60 | 60 // round away from zero |
61 case BigDecimal.ROUND_FLOOR: | |
62 // round toward negative infinity | |
63 return !isNegative; | |
64 | |
65 case BigDecimal.ROUND_HALF_UP: | |
66 switch (section) { | |
67 case SECTION_MIDPOINT: | |
68 return false; | 61 return false; |
69 case SECTION_LOWER: | 62 |
| 63 case BigDecimal.ROUND_DOWN: |
| 64 // round toward zero |
70 return true; | 65 return true; |
71 case SECTION_UPPER: | 66 |
| 67 case BigDecimal.ROUND_CEILING: |
| 68 // round toward positive infinity |
| 69 return isNegative; |
| 70 |
| 71 case BigDecimal.ROUND_FLOOR: |
| 72 // round toward negative infinity |
| 73 return !isNegative; |
| 74 |
| 75 case BigDecimal.ROUND_HALF_UP: |
| 76 switch (section) { |
| 77 case SECTION_MIDPOINT: |
| 78 return false; |
| 79 case SECTION_LOWER: |
| 80 return true; |
| 81 case SECTION_UPPER: |
| 82 return false; |
| 83 } |
| 84 break; |
| 85 |
| 86 case BigDecimal.ROUND_HALF_DOWN: |
| 87 switch (section) { |
| 88 case SECTION_MIDPOINT: |
| 89 return true; |
| 90 case SECTION_LOWER: |
| 91 return true; |
| 92 case SECTION_UPPER: |
| 93 return false; |
| 94 } |
| 95 break; |
| 96 |
| 97 case BigDecimal.ROUND_HALF_EVEN: |
| 98 switch (section) { |
| 99 case SECTION_MIDPOINT: |
| 100 return isEven; |
| 101 case SECTION_LOWER: |
| 102 return true; |
| 103 case SECTION_UPPER: |
| 104 return false; |
| 105 } |
| 106 break; |
| 107 } |
| 108 |
| 109 // Rounding mode UNNECESSARY |
| 110 throw new ArithmeticException("Rounding is required on " + reference.toS
tring()); |
| 111 } |
| 112 |
| 113 /** |
| 114 * Gets whether the given rounding mode's rounding boundary is at the midpoi
nt. The rounding boundary |
| 115 * is the point at which a number switches from being rounded down to being
rounded up. For example, |
| 116 * with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundar
y is at the midpoint, and |
| 117 * this function would return true. However, for UP, DOWN, CEILING, and FLOO
R, the rounding boundary |
| 118 * is at the "edge", and this function would return false. |
| 119 * |
| 120 * @param roundingMode |
| 121 * The integer version of the {@link RoundingMode}. |
| 122 * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false
otherwise. |
| 123 */ |
| 124 public static boolean roundsAtMidpoint(int roundingMode) { |
| 125 switch (roundingMode) { |
| 126 case BigDecimal.ROUND_UP: |
| 127 case BigDecimal.ROUND_DOWN: |
| 128 case BigDecimal.ROUND_CEILING: |
| 129 case BigDecimal.ROUND_FLOOR: |
72 return false; | 130 return false; |
73 } | 131 |
74 break; | 132 default: |
75 | |
76 case BigDecimal.ROUND_HALF_DOWN: | |
77 switch (section) { | |
78 case SECTION_MIDPOINT: | |
79 return true; | 133 return true; |
80 case SECTION_LOWER: | 134 } |
81 return true; | 135 } |
82 case SECTION_UPPER: | 136 |
83 return false; | 137 private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED =
new MathContext[RoundingMode |
84 } | 138 .values().length]; |
85 break; | 139 |
86 | 140 private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS =
new MathContext[RoundingMode |
87 case BigDecimal.ROUND_HALF_EVEN: | 141 .values().length]; |
88 switch (section) { | 142 |
89 case SECTION_MIDPOINT: | 143 static { |
90 return isEven; | 144 for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS.length; i++)
{ |
91 case SECTION_LOWER: | 145 MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[i] = new MathContext(0, Roun
dingMode.valueOf(i)); |
92 return true; | 146 MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[i] = new MathContext(34); |
93 case SECTION_UPPER: | 147 } |
94 return false; | 148 } |
95 } | 149 |
96 break; | 150 /** |
97 } | 151 * Gets the user-specified math context out of the property bag. If there is
none, falls back to a |
98 | 152 * math context with unlimited precision and the user-specified rounding mod
e, which defaults to |
99 // Rounding mode UNNECESSARY | 153 * HALF_EVEN (the IEEE 754R default). |
100 throw new ArithmeticException("Rounding is required on " + reference.toStrin
g()); | 154 * |
101 } | 155 * @param properties |
102 | 156 * The property bag. |
103 /** | 157 * @return A {@link MathContext}. Never null. |
104 * Gets whether the given rounding mode's rounding boundary is at the midpoint
. The rounding | 158 */ |
105 * boundary is the point at which a number switches from being rounded down to
being rounded up. | 159 public static MathContext getMathContextOrUnlimited(DecimalFormatProperties
properties) { |
106 * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the round
ing boundary is at | 160 MathContext mathContext = properties.getMathContext(); |
107 * the midpoint, and this function would return true. However, for UP, DOWN, C
EILING, and FLOOR, | 161 if (mathContext == null) { |
108 * the rounding boundary is at the "edge", and this function would return fals
e. | 162 RoundingMode roundingMode = properties.getRoundingMode(); |
109 * | 163 if (roundingMode == null) |
110 * @param roundingMode The integer version of the {@link RoundingMode}. | 164 roundingMode = RoundingMode.HALF_EVEN; |
111 * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false ot
herwise. | 165 mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[roundingMode.o
rdinal()]; |
112 */ | 166 } |
113 public static boolean roundsAtMidpoint(int roundingMode) { | 167 return mathContext; |
114 switch (roundingMode) { | 168 } |
115 case BigDecimal.ROUND_UP: | 169 |
116 case BigDecimal.ROUND_DOWN: | 170 /** |
117 case BigDecimal.ROUND_CEILING: | 171 * Gets the user-specified math context out of the property bag. If there is
none, falls back to a |
118 case BigDecimal.ROUND_FLOOR: | 172 * math context with 34 digits of precision (the 128-bit IEEE 754R default)
and the user-specified |
119 return false; | 173 * rounding mode, which defaults to HALF_EVEN (the IEEE 754R default). |
120 | 174 * |
121 default: | 175 * @param properties |
122 return true; | 176 * The property bag. |
123 } | 177 * @return A {@link MathContext}. Never null. |
124 } | 178 */ |
125 | 179 public static MathContext getMathContextOr34Digits(DecimalFormatProperties p
roperties) { |
126 private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED = | 180 MathContext mathContext = properties.getMathContext(); |
127 new MathContext[RoundingMode.values().length]; | 181 if (mathContext == null) { |
128 | 182 RoundingMode roundingMode = properties.getRoundingMode(); |
129 private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS = | 183 if (roundingMode == null) |
130 new MathContext[RoundingMode.values().length]; | 184 roundingMode = RoundingMode.HALF_EVEN; |
131 | 185 mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[roundingMode.o
rdinal()]; |
132 static { | 186 } |
133 for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS.length; i++) { | 187 return mathContext; |
134 MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[i] = new MathContext(0, RoundingMo
de.valueOf(i)); | 188 } |
135 MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[i] = new MathContext(34); | 189 |
136 } | 190 /** |
137 } | 191 * Gets a MathContext with unlimited precision and the specified RoundingMod
e. Equivalent to "new |
138 | 192 * MathContext(0, roundingMode)", but pulls from a singleton to prevent obje
ct thrashing. |
139 /** | 193 * |
140 * Gets the user-specified math context out of the property bag. If there is n
one, falls back to a | 194 * @param roundingMode |
141 * math context with unlimited precision and the user-specified rounding mode,
which defaults to | 195 * The {@link RoundingMode} to use. |
142 * HALF_EVEN (the IEEE 754R default). | 196 * @return The corresponding {@link MathContext}. |
143 * | 197 */ |
144 * @param properties The property bag. | 198 public static MathContext mathContextUnlimited(RoundingMode roundingMode) { |
145 * @return A {@link MathContext}. Never null. | 199 return MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[roundingMode.ordinal()]; |
146 */ | 200 } |
147 public static MathContext getMathContextOrUnlimited(DecimalFormatProperties pr
operties) { | |
148 MathContext mathContext = properties.getMathContext(); | |
149 if (mathContext == null) { | |
150 RoundingMode roundingMode = properties.getRoundingMode(); | |
151 if (roundingMode == null) roundingMode = RoundingMode.HALF_EVEN; | |
152 mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[roundingMode.ordinal
()]; | |
153 } | |
154 return mathContext; | |
155 } | |
156 | |
157 /** | |
158 * Gets the user-specified math context out of the property bag. If there is n
one, falls back to a | |
159 * math context with 34 digits of precision (the 128-bit IEEE 754R default) an
d the user-specified | |
160 * rounding mode, which defaults to HALF_EVEN (the IEEE 754R default). | |
161 * | |
162 * @param properties The property bag. | |
163 * @return A {@link MathContext}. Never null. | |
164 */ | |
165 public static MathContext getMathContextOr34Digits(DecimalFormatProperties pro
perties) { | |
166 MathContext mathContext = properties.getMathContext(); | |
167 if (mathContext == null) { | |
168 RoundingMode roundingMode = properties.getRoundingMode(); | |
169 if (roundingMode == null) roundingMode = RoundingMode.HALF_EVEN; | |
170 mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[roundingMode.ordinal
()]; | |
171 } | |
172 return mathContext; | |
173 } | |
174 | |
175 /** | |
176 * Gets a MathContext with unlimited precision and the specified RoundingMode.
Equivalent to "new | |
177 * MathContext(0, roundingMode)", but pulls from a singleton to prevent object
thrashing. | |
178 * | |
179 * @param roundingMode The {@link RoundingMode} to use. | |
180 * @return The corresponding {@link MathContext}. | |
181 */ | |
182 public static MathContext mathContextUnlimited(RoundingMode roundingMode) { | |
183 return MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[roundingMode.ordinal()]; | |
184 } | |
185 } | 201 } |
LEFT | RIGHT |