OLD | NEW |
(Empty) | |
| 1 // © 2017 and later: Unicode, Inc. and others. |
| 2 // License & terms of use: http://www.unicode.org/copyright.html#License |
| 3 package com.ibm.icu.impl.number.rounders; |
| 4 |
| 5 import java.math.RoundingMode; |
| 6 |
| 7 import com.ibm.icu.impl.number.FormatQuantity; |
| 8 import com.ibm.icu.impl.number.Properties; |
| 9 import com.ibm.icu.impl.number.Rounder; |
| 10 |
| 11 public class SignificantDigitsRounder extends Rounder { |
| 12 |
| 13 /** |
| 14 * Sets whether the minimum significant digits should override the maximum int
eger and fraction |
| 15 * digits. This affects both display and rounding. Default is true. |
| 16 * |
| 17 * <p>For example, if this option is enabled, formatting the number 4.567 with
3 min/max |
| 18 * significant digits against the pattern "0.0" (1 min/max fraction digits) wi
ll result in "4.57" |
| 19 * in locale <em>en-US</em> with the default rounding mode. If this option is
disabled, the max |
| 20 * fraction digits take priority instead, and the output will be "4.6". |
| 21 * |
| 22 * @param significantDigitsOverride true to ensure that the minimum significan
t digits are always |
| 23 * shown; false to ensure that the maximum integer and fraction digits are
obeyed. |
| 24 * @return The property bag, for chaining. |
| 25 */ |
| 26 public static enum SignificantDigitsMode { |
| 27 OVERRIDE_MAXIMUM_FRACTION, |
| 28 RESPECT_MAXIMUM_FRACTION, |
| 29 ENSURE_MINIMUM_SIGNIFICANT |
| 30 }; |
| 31 |
| 32 public static interface IProperties extends IBasicRoundingProperties { |
| 33 |
| 34 static int DEFAULT_MINIMUM_SIGNIFICANT_DIGITS = -1; |
| 35 |
| 36 /** @see #setMinimumSignificantDigits */ |
| 37 public int getMinimumSignificantDigits(); |
| 38 |
| 39 /** |
| 40 * Sets the minimum number of significant digits to display. If, after round
ing to the number of |
| 41 * significant digits specified by {@link #setMaximumSignificantDigits}, the
number of remaining |
| 42 * significant digits is less than the minimum, the number will be padded wi
th zeros. For |
| 43 * example, if minimum significant digits is 3, the number 5.8 will be forma
tted as "5.80" in |
| 44 * locale <em>en-US</em>. Note that minimum significant digits is relevant o
nly when numbers |
| 45 * have digits after the decimal point. |
| 46 * |
| 47 * <p>If both minimum significant digits and minimum integer/fraction digits
are set at the same |
| 48 * time, both values will be respected, and the one that results in the grea
ter number of |
| 49 * padding zeros will be used. For example, formatting the number 73 with 3
minimum significant |
| 50 * digits and 2 minimum fraction digits will produce "73.00". |
| 51 * |
| 52 * <p>The number of significant digits can be specified in a pattern string
using the '@' |
| 53 * character. For example, the pattern "@@#" corresponds to a minimum of 2 a
nd a maximum of 3 |
| 54 * significant digits. |
| 55 * |
| 56 * @param minimumSignificantDigits The minimum number of significant digits
to display. |
| 57 * @return The property bag, for chaining. |
| 58 */ |
| 59 public IProperties setMinimumSignificantDigits(int minimumSignificantDigits)
; |
| 60 |
| 61 static int DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS = -1; |
| 62 |
| 63 /** @see #setMaximumSignificantDigits */ |
| 64 public int getMaximumSignificantDigits(); |
| 65 |
| 66 /** |
| 67 * Sets the maximum number of significant digits to display. The number of s
ignificant digits is |
| 68 * equal to the number of digits counted from the leftmost nonzero digit thr
ough the rightmost |
| 69 * nonzero digit; for example, the number "2010" has 3 significant digits. I
f the number has |
| 70 * more significant digits than specified here, the extra significant digits
will be rounded off |
| 71 * using the rounding mode specified by {@link #setRoundingMode(RoundingMode
)}. For example, if |
| 72 * maximum significant digits is 3, the number 1234.56 will be formatted as
"1230" in locale |
| 73 * <em>en-US</em> with the default rounding mode. |
| 74 * |
| 75 * <p>If both maximum significant digits and maximum integer/fraction digits
are set at the same |
| 76 * time, the behavior is undefined. |
| 77 * |
| 78 * <p>The number of significant digits can be specified in a pattern string
using the '@' |
| 79 * character. For example, the pattern "@@#" corresponds to a minimum of 2 a
nd a maximum of 3 |
| 80 * significant digits. |
| 81 * |
| 82 * @param maximumSignificantDigits The maximum number of significant digits
to display. |
| 83 * @return The property bag, for chaining. |
| 84 */ |
| 85 public IProperties setMaximumSignificantDigits(int maximumSignificantDigits)
; |
| 86 |
| 87 static SignificantDigitsMode DEFAULT_SIGNIFICANT_DIGITS_MODE = null; |
| 88 |
| 89 /** @see #setSignificantDigitsMode */ |
| 90 public SignificantDigitsMode getSignificantDigitsMode(); |
| 91 |
| 92 /** |
| 93 * Sets the strategy used when reconciling significant digits versus integer
and fraction |
| 94 * lengths. |
| 95 * |
| 96 * @param significantDigitsMode One of the options from {@link SignificantDi
gitsMode}. |
| 97 * @return The property bag, for chaining. |
| 98 */ |
| 99 public IProperties setSignificantDigitsMode(SignificantDigitsMode significan
tDigitsMode); |
| 100 } |
| 101 |
| 102 public static boolean useSignificantDigits(IProperties properties) { |
| 103 return properties.getMinimumSignificantDigits() |
| 104 != IProperties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS |
| 105 || properties.getMaximumSignificantDigits() |
| 106 != IProperties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS |
| 107 || properties.getSignificantDigitsMode() != IProperties.DEFAULT_SIGNIFIC
ANT_DIGITS_MODE; |
| 108 } |
| 109 |
| 110 public static SignificantDigitsRounder getInstance(IProperties properties) { |
| 111 return new SignificantDigitsRounder(properties); |
| 112 } |
| 113 |
| 114 private final int minSig; |
| 115 private final int maxSig; |
| 116 private final SignificantDigitsMode mode; |
| 117 |
| 118 private SignificantDigitsRounder(IProperties properties) { |
| 119 super(properties); |
| 120 int _minSig = properties.getMinimumSignificantDigits(); |
| 121 int _maxSig = properties.getMaximumSignificantDigits(); |
| 122 minSig = _minSig < 1 ? 1 : _minSig > 1000 ? 1000 : _minSig; |
| 123 maxSig = _maxSig < 0 ? 1000 : _maxSig < minSig ? minSig : _maxSig > 1000 ? 1
000 : _maxSig; |
| 124 SignificantDigitsMode _mode = properties.getSignificantDigitsMode(); |
| 125 mode = _mode == null ? SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION : _mo
de; |
| 126 } |
| 127 |
| 128 @Override |
| 129 public void apply(FormatQuantity input) { |
| 130 |
| 131 int magnitude, effectiveMag, magMinSig, magMaxSig; |
| 132 |
| 133 if (input.isZero()) { |
| 134 // Treat zero as if magnitude corresponded to the minimum number of zeros |
| 135 magnitude = minInt - 1; |
| 136 } else { |
| 137 magnitude = input.getMagnitude(); |
| 138 } |
| 139 effectiveMag = Math.min(magnitude + 1, maxInt); |
| 140 magMinSig = effectiveMag - minSig; |
| 141 magMaxSig = effectiveMag - maxSig; |
| 142 |
| 143 // Step 1: pick the rounding magnitude and apply. |
| 144 int roundingMagnitude; |
| 145 switch (mode) { |
| 146 case OVERRIDE_MAXIMUM_FRACTION: |
| 147 // Always round to maxSig. |
| 148 // Of the six possible orders: |
| 149 // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins |
| 150 // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins |
| 151 // Case 3: minSig, minFrac, maxFrac, maxSig -- maxSig wins |
| 152 // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins |
| 153 // Case 5: minFrac, minSig, maxFrac, maxSig -- maxSig wins |
| 154 // Case 6: minFrac, maxFrac, minSig, maxSig -- maxSig wins |
| 155 roundingMagnitude = magMaxSig; |
| 156 break; |
| 157 case RESPECT_MAXIMUM_FRACTION: |
| 158 // Round to the strongest of maxFrac, maxInt, and maxSig. |
| 159 // Of the six possible orders: |
| 160 // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins |
| 161 // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins |
| 162 // Case 3: minSig, minFrac, maxFrac, maxSig -- maxFrac wins --> diffe
rs from default |
| 163 // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins |
| 164 // Case 5: minFrac, minSig, maxFrac, maxSig -- maxFrac wins --> diffe
rs from default |
| 165 // Case 6: minFrac, maxFrac, minSig, maxSig -- maxFrac wins --> diffe
rs from default |
| 166 // |
| 167 // Math.max() picks the rounding magnitude farthest to the left (most si
gnificant). |
| 168 // Math.min() picks the rounding magnitude farthest to the right (least
significant). |
| 169 roundingMagnitude = Math.max(-maxFrac, magMaxSig); |
| 170 break; |
| 171 case ENSURE_MINIMUM_SIGNIFICANT: |
| 172 // Round to the strongest of maxFrac and maxSig, and always ensure minSi
g. |
| 173 // Of the six possible orders: |
| 174 // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins |
| 175 // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins |
| 176 // Case 3: minSig, minFrac, maxFrac, maxSig -- maxFrac wins --> diffe
rs from default |
| 177 // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins |
| 178 // Case 5: minFrac, minSig, maxFrac, maxSig -- maxFrac wins --> diffe
rs from default |
| 179 // Case 6: minFrac, maxFrac, minSig, maxSig -- minSig wins --> differ
s from default |
| 180 roundingMagnitude = Math.min(magMinSig, Math.max(-maxFrac, magMaxSig)); |
| 181 break; |
| 182 default: |
| 183 throw new AssertionError(); |
| 184 } |
| 185 input.roundToMagnitude(roundingMagnitude, mathContext); |
| 186 |
| 187 // In case magnitude changed: |
| 188 if (input.isZero()) { |
| 189 magnitude = minInt - 1; |
| 190 } else { |
| 191 magnitude = input.getMagnitude(); |
| 192 } |
| 193 effectiveMag = Math.min(magnitude + 1, maxInt); |
| 194 magMinSig = effectiveMag - minSig; |
| 195 magMaxSig = effectiveMag - maxSig; |
| 196 |
| 197 // Step 2: pick the number of visible digits. |
| 198 switch (mode) { |
| 199 case OVERRIDE_MAXIMUM_FRACTION: |
| 200 // Ensure minSig is always displayed. |
| 201 input.setIntegerFractionLength( |
| 202 minInt, maxInt, Math.max(minFrac, -magMinSig), Integer.MAX_VALUE); |
| 203 break; |
| 204 case RESPECT_MAXIMUM_FRACTION: |
| 205 // Ensure minSig is displayed, unless doing so is in violation of maxFra
c. |
| 206 input.setIntegerFractionLength( |
| 207 minInt, maxInt, Math.min(maxFrac, Math.max(minFrac, -magMinSig)), ma
xFrac); |
| 208 break; |
| 209 case ENSURE_MINIMUM_SIGNIFICANT: |
| 210 // Follow minInt/minFrac, but ensure all digits are allowed to be visibl
e. |
| 211 input.setIntegerFractionLength(minInt, maxInt, minFrac, Integer.MAX_VALU
E); |
| 212 break; |
| 213 } |
| 214 } |
| 215 |
| 216 @Override |
| 217 public void export(Properties properties) { |
| 218 super.export(properties); |
| 219 properties.setMinimumSignificantDigits(minSig); |
| 220 properties.setMaximumSignificantDigits(maxSig); |
| 221 properties.setSignificantDigitsMode(mode); |
| 222 } |
| 223 } |
OLD | NEW |