set pattern 0%
begin
parse output breaks
-// S uses a MathContext of 16 digits when dividing by 100.
-9223372036854775807% 92233720368547758.07 S
+9223372036854775807% 92233720368547758.07
// We need to use a math context in order to prevent non-terminating decimal expansions.
// This is only used when dividing by the multiplier.
- MathContext mc = RoundingUtils.getMathContextOr16Digits(properties);
+ MathContext mc = RoundingUtils.getMathContextOr34Digits(properties);
// Construct the output number.
// This is the only step during fast-mode parsing that incurs object creations.
private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED =
new MathContext[RoundingMode.values().length];
- private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS =
+ private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS =
new MathContext[RoundingMode.values().length];
static {
- for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS.length; i++) {
+ for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS.length; i++) {
MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[i] = new MathContext(0, RoundingMode.valueOf(i));
- MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS[i] = new MathContext(16);
+ MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[i] = new MathContext(34);
}
}
/**
* Gets the user-specified math context out of the property bag. If there is none, falls back to a
- * math context with 16 digits of precision (the 64-bit IEEE 754R default) and the user-specified
+ * math context with 34 digits of precision (the 128-bit IEEE 754R default) and the user-specified
* rounding mode, which defaults to HALF_EVEN (the IEEE 754R default).
*
* @param properties The property bag.
* @return A {@link MathContext}. Never null.
*/
- public static MathContext getMathContextOr16Digits(IBasicRoundingProperties properties) {
+ public static MathContext getMathContextOr34Digits(IBasicRoundingProperties properties) {
MathContext mathContext = properties.getMathContext();
if (mathContext == null) {
RoundingMode roundingMode = properties.getRoundingMode();
if (roundingMode == null) roundingMode = RoundingMode.HALF_EVEN;
- mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS[roundingMode.ordinal()];
+ mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[roundingMode.ordinal()];
}
return mathContext;
}
/**
* {@icu} <strong>Rounding and Digit Limits:</strong> Sets the {@link java.math.MathContext} used
* to round numbers. A "math context" encodes both a rounding mode and a number of significant
- * digits.
- *
- * <p>This method is provided for users who require their output to conform to a standard math
- * context. <strong>Most users should call {@link #setRoundingMode} and/or {@link
- * #setMaximumSignificantDigits} instead of this method.</strong>
+ * digits. Most users should call {@link #setRoundingMode} and/or {@link
+ * #setMaximumSignificantDigits} instead of this method.
+ *
+ * <p>When formatting, since no division is ever performed, the default MathContext is unlimited
+ * significant digits. However, when division occurs during parsing to correct for percentages and
+ * multipliers, a MathContext of 34 digits, the IEEE 754R Decimal128 standard, is used by default.
+ * If you require more than 34 digits when parsing, you can set a custom MathContext using this
+ * method.
*
* @param mathContext The MathContext to use when rounding numbers.
* @see java.math.MathContext
set pattern 0%
begin
parse output breaks
-// S uses a MathContext of 16 digits when dividing by 100.
-9223372036854775807% 92233720368547758.07 S
+9223372036854775807% 92233720368547758.07
}
}
+ @Test
+ public void TestSetMathContext() throws ParseException {
+ java.math.MathContext fourDigits = new java.math.MathContext(4);
+ java.math.MathContext unlimitedCeiling = new java.math.MathContext(0, RoundingMode.CEILING);
+
+ // Test rounding
+ DecimalFormat df = new DecimalFormat();
+ assertEquals("Default format", "9,876.543", df.format(9876.5432));
+ df.setMathContext(fourDigits);
+ assertEquals("Format with fourDigits", "9,877", df.format(9876.5432));
+ df.setMathContext(unlimitedCeiling);
+ assertEquals("Format with unlimitedCeiling", "9,876.544", df.format(9876.5432));
+
+ // Test multiplication
+ df = new DecimalFormat("0.000%");
+ assertEquals("Default multiplication", "12.001%", df.format(0.120011));
+ df.setMathContext(fourDigits);
+ assertEquals("Multiplication with fourDigits", "12.000%", df.format(0.120011));
+ df.setMathContext(unlimitedCeiling);
+ assertEquals("Multiplication with unlimitedCeiling", "12.002%", df.format(0.120011));
+
+ // Test simple division
+ df = new DecimalFormat("0%");
+ assertEquals("Default division", 0.12001, df.parse("12.001%").doubleValue());
+ df.setMathContext(fourDigits);
+ assertEquals("Division with fourDigits", 0.12, df.parse("12.001%").doubleValue());
+ df.setMathContext(unlimitedCeiling);
+ assertEquals("Division with unlimitedCeiling", 0.12001, df.parse("12.001%").doubleValue());
+
+ // Test extreme division
+ df = new DecimalFormat();
+ df.setMultiplier(1000000007); // prime number
+ String hugeNumberString = "9876543212345678987654321234567898765432123456789"; // 49 digits
+ BigInteger huge34Digits = new BigInteger("9876543143209876985185182338271622000000");
+ BigInteger huge4Digits = new BigInteger("9877000000000000000000000000000000000000");
+ assertEquals("Default extreme division", huge34Digits, df.parse(hugeNumberString));
+ df.setMathContext(fourDigits);
+ assertEquals("Extreme division with fourDigits", huge4Digits, df.parse(hugeNumberString));
+ df.setMathContext(unlimitedCeiling);
+ try {
+ df.parse(hugeNumberString);
+ fail("Extreme division with unlimitedCeiling should throw ArithmeticException");
+ } catch (ArithmeticException e) {
+ // expected
+ }
+ }
+
@Test
public void Test10436() {
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);