src/share/classes/java/text/DecimalFormat.java
Print this page
@@ -43,10 +43,11 @@
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Currency;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -517,13 +518,27 @@
* @return The formatted number string
* @see java.text.FieldPosition
*/
public StringBuffer format(double number, StringBuffer result,
FieldPosition fieldPosition) {
+ // If fieldPosition is a DontCareFieldPosition instance we can try to go to fast-path code.
+ boolean tryFastPath = false;
+ if (fieldPosition == DontCareFieldPosition.INSTANCE) tryFastPath = true;
+ else {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
+ }
+ if (tryFastPath) {
+ String tempResult = fastFormat(number);
+ if (tempResult != null) {
+ result.append(tempResult);
+ return result;
+ }
+ }
+
+ // if fast path could not work, we fallback to standard code.
return format(number, result, fieldPosition.getFieldDelegate());
}
/**
* Formats a double to produce a string.
@@ -855,10 +870,596 @@
"Cannot format given Object as a Number");
}
return delegate.getIterator(sb.toString());
}
+ // Fast path formating algorithm for double ================================
+
+ // Fast-path formatting will be used for format(double) if a number of
+ // conditions are met (see checkAndeStFastPathStatus()):
+ // - Only if instance properties meet the right predefined conditions
+ // - The abs value of the double to format is <= Integer.MAX_VALUE
+
+ // ------ The fast path for double methods -------
+
+ // Decides if rounding up must occur for the passed double.
+ private boolean toBeHalfEvenRounded(double number,
+ int integralPartAsInt, int nbIntegerDigits,
+ int fractionalPartAsInt, int nbFractionalDigits) {
+ // Index of undecidable rounding interval depending on magnitude of number.
+ int boundIndex = 0;
+ if (integralPartAsInt != 0)
+ boundIndex = maximumFractionDigits + nbIntegerDigits;
+ else if (fractionalPartAsInt != 0) boundIndex = nbFractionalDigits;
+
+ // Check poisition of number relative to "Undecidable Rounding Interval".
+ if (remainingFractionalPart <= safeMinusRoundingBounds[boundIndex])
+ return false; // number is outside below.
+ else if (remainingFractionalPart >= safeMajorRoundingBounds[boundIndex])
+ return true; // number is outside above.
+ else {
+ // number is inside. We compare against nearest perfect-half.
+ double perfectHalf;
+ if (isCurrencyFormat)
+ perfectHalf = (((double) integralPartAsInt * 1000.0d) +
+ (double) (fractionalPartAsInt * 10 + 5)) / 1000.0d;
+ else
+ perfectHalf = (((double) integralPartAsInt * 10000.0d) +
+ (double) (fractionalPartAsInt * 10 + 5)) / 10000.0d;
+
+ // Applies healf-even rounding rule.
+ if (number == perfectHalf)
+ return (((fractionalPartAsInt % 10) & 1) != 0);
+ else return (number > perfectHalf);
+ }
+ }
+
+ // Returns the exact number of digits (radix 10) representing
+ // the passed paramater i. Sets utility formating fields used in the algorithm
+ private int intStringSize(int i) {
+ if (i <= 99999) {
+ if (i <= 999) {
+ if (i <= 99) {
+ if (i <= 9) {
+ if (i == 9) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 0;
+ return 1;
+ }
+ else {
+ if (i == 99) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 0;
+ return 2;
+ }
+ }
+ else {
+ if (i == 999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 0;
+ return 3;
+ }
+ }
+ else if (i <= 9999) {
+ if (i == 9999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 1;
+ return 4;
+ }
+ else {
+ if (i == 99999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 1;
+ return 5;
+ }
+ }
+ else {
+ if (i <= 99999999) {
+ if (i <= 999999) {
+ if (i == 999999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 1;
+ return 6;
+ }
+ else if (i <= 9999999) {
+ if (i == 9999999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 2;
+ return 7;
+ }
+ else {
+ if (i == 99999999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 2;
+ return 8;
+ }
+ }
+ else if (i <= 999999999) {
+ if (i == 999999999) isAllNines = true;
+ else isAllNines = false;
+ nbGroupsNeeded = 2;
+ return 9;
+ }
+ else {
+ isAllNines = false;
+ nbGroupsNeeded = 3;
+ return 10;
+ }
+ }
+ }
+
+ // Returns the exact number of digits (radix 10) representing the passed i,
+ // Sets utility formating fields used in the algorithm
+ private int fracStringSize(int i) {
+ if (i > 99) {
+ if (i == 999) isAllNines = true;
+ return 3;
+ }
+ else if (i < 10) {
+ if (i == 9) isAllNines = true;
+ return 1;
+ }
+ else {
+ if (i == 99) isAllNines = true;
+ return 2;
+ }
+ }
+
+
+ // Extracts and returns the 2 (Currency pattern) or 3 (Decimal pattern)
+ // digits int representing the fractional part of passed double.
+ private int getFracIntegral(double fractional, int nbFractionalDigits) {
+ int fractAsInt;
+ double nextFractional;
+ if (nbFractionalDigits == 2) {
+ // Default currency pattern case. 2 fractional digits. Scale 100.0d.
+ nextFractional = fractional * 100.0d;
+ fractAsInt = (int) nextFractional;
+ }
+ else {
+ // Default decimal pattern case. 3 fractional digits. Scale 1000.0d
+ nextFractional = fractional * 1000.0d;
+ fractAsInt = (int) nextFractional;
+ }
+
+ // The remaining fractional part that will be used in rounding decision ...
+ remainingFractionalPart = nextFractional - (double) fractAsInt;
+
+ return fractAsInt;
+ }
+
+
+ // Check validity of using fast path for this instance.
+ // If fast-path is valid for this instance, sets fast-path state as true
+ // and initialize fast-path utility fields as needed.
+ //
+ // FAST-PATH RULES:
+ // Similar to the default DecimalFormat instantiation case. More precisely:
+ // - HALF_EVEN rounding mode,
+ // - isGroupingUsed() is true,
+ // - groupingSize of 3,
+ // - multiplier is 1,
+ // - Decimal separator not mandatory,
+ // - No use of exponential notation,
+ // - minimumIntegerDigits is exactly 1 and maximumIntegerDigits at least 10
+ // - for number of fractional digits, the exact values found in the default case:
+ // Currency : min = max = 2.
+ // Decimal : min = 0. max = 3.
+ private void checkAndSetFastPathStatus() {
+ boolean fastPathWasOn = isFastPath;
+ if ((roundingMode == RoundingMode.HALF_EVEN) &&
+ (isGroupingUsed()) &&
+ (groupingSize == 3) &&
+ (multiplier == 1) &&
+ (!decimalSeparatorAlwaysShown) &&
+ (!useExponentialNotation)) {
+
+ // The fast-path algorithm is semi-hardcoded against
+ // minimumIntegerDigits and maximumIntegerDigits.
+ if ((minimumIntegerDigits == 1) &&
+ (maximumIntegerDigits >= 10)) isFastPath = true;
+ else isFastPath = false;
+
+ // The fast-path algorithm is hardcoded against
+ // minimumFractionDigits and maximumFractionDigits.
+ if (isFastPath) {
+ if (isCurrencyFormat) {
+ if ((minimumFractionDigits != 2) ||
+ (maximumFractionDigits != 2)) isFastPath = false;
+ }
+ else if ((minimumFractionDigits != 0) ||
+ (maximumFractionDigits != 3)) isFastPath = false;
+ }
+ }
+ else isFastPath = false;
+
+ if (isFastPath) {
+ // Sets up the locale specific values used when formatting.
+ zeroDelta = symbols.getZeroDigit() - '0'; // '0' is our default representation of zero.
+ groupingChar = symbols.getGroupingSeparator();
+ decimalSeparatorChar = isCurrencyFormat ?
+ symbols.getMonetaryDecimalSeparator() :
+ symbols.getDecimalSeparator();
+
+ // Instantiate and initialize char container arrays used in formatting.
+ int maxNbIntegralDigits = 10;
+ int maxNbGroups = 3;
+
+ // Max size for positive numbers.
+ int maxContainerSize =
+ positivePrefix.length() +
+ maxNbIntegralDigits +
+ maxNbGroups +
+ 1 + maximumFractionDigits +
+ positiveSuffix.length();
+
+ char[] template = new char[maxContainerSize];
+ positivePrefix.getChars(0, positivePrefix.length(), template, 0);
+ positiveFastPathContainer = template;
+
+ // Max size for negative numbers.
+ maxContainerSize =
+ negativePrefix.length() +
+ maxNbIntegralDigits +
+ maxNbGroups +
+ 1 + maximumFractionDigits +
+ negativeSuffix.length();
+
+ template = new char[maxContainerSize];
+ negativePrefix.getChars(0, negativePrefix.length(), template, 0);
+ negativeFastPathContainer = template;
+
+ // Initialize suffix char arrays.
+ charsPositiveSuffix = positiveSuffix.toCharArray();
+ charsNegativeSuffix = negativeSuffix.toCharArray();
+ isSuffixPresent =
+ (charsPositiveSuffix.length != 0) || (charsNegativeSuffix.length != 0);
+
+ // Initialize rounding interval constants.
+ if (isCurrencyFormat) {
+ safeMinusRoundingBounds = CurrencySafeMinusRoundingBounds;
+ safeMajorRoundingBounds = CurrencySafeMajorRoundingBounds;
+ }
+ else {
+ safeMinusRoundingBounds = StdSafeMinusRoundingBounds;
+ safeMajorRoundingBounds = StdSafeMajorRoundingBounds;
+ }
+ }
+ else if (fastPathWasOn) {
+ // Previous state was fast-path and is no more. Resets cached constants.
+ positiveFastPathContainer = null;
+ negativeFastPathContainer = null;
+ charsPositiveSuffix = null;
+ charsNegativeSuffix = null;
+ safeMajorRoundingBounds = null;
+ safeMinusRoundingBounds = null;
+ }
+
+ fastPathCheckNeeded = false;
+ }
+
+ // Fills efficiently the passed digitsBuffer with digit for up to 4 chars
+ // for length, starting at startIndex. len must be positive and at most 4.
+ private void fillUpTo4Chars(char[] digitsBuffer, int startIndex,
+ int len, char digit) {
+ int lowerIndex = startIndex;
+ if (len == 1) digitsBuffer[lowerIndex] = digit;
+ else {
+ int upperIndex = lowerIndex + len - 1; // included !
+ digitsBuffer[upperIndex] = digit;
+ digitsBuffer[lowerIndex] = digit;
+
+ if (len > 2) {
+ // Fill the remaining 1 or 2 digits.
+ digitsBuffer[++lowerIndex] = digit; // at least one additional digit to fill
+ if (--upperIndex > lowerIndex) digitsBuffer[upperIndex] = digit;
+ }
+ }
+ }
+
+ // Appends positive or negative suffix, depending on sign. Updates lastUsedIndex.
+ private void appendSuffix(boolean isNegative, char[] internalChars) {
+ // Init to factorise on suffix code
+ char[] suffix;
+ if (isNegative)
+ suffix = charsNegativeSuffix;
+ else suffix = charsPositiveSuffix;
+ int len = suffix.length;
+
+ // Trivial and most frequent case.
+ if (len == 0) return;
+
+ // We need to append something.
+ int startIndex = lastUsedIndex + 1;
+ if (len == 1) internalChars[startIndex] = suffix[0];
+ else if (len <= 4) {
+ int dstLower = startIndex;
+ int dstUpper = dstLower + len - 1;
+ int srcUpper = len - 1;
+ internalChars[dstLower++] = suffix[0];
+ internalChars[dstUpper--] = suffix[srcUpper];
+
+ if (len > 2) internalChars[dstLower] = suffix[1];
+ if (len == 4) internalChars[dstUpper] = suffix[2];
+ }
+ else System.arraycopy(suffix, 0, internalChars, startIndex, len);
+
+ lastUsedIndex += len;
+ }
+
+ // Converts digit chars to current locale.
+ // Loops downward starting from LastUsedIndex.
+ private void localizeDigits(char[] digitsBuffer, int startIndex) {
+ // We localize digits only, taking into account currency/decimal fractional case
+ int nonDigitCounter = maximumFractionDigits;
+ for (int cursor = lastUsedIndex; cursor >= startIndex; cursor--) {
+ if (nonDigitCounter != 0) {
+ // Cursor char is a digit char and we must localize it.
+ digitsBuffer[cursor] += zeroDelta;
+ nonDigitCounter--;
+ }
+ else {
+ // Cursor char is decimal separator or grouping char. Just reinit counter.
+ nonDigitCounter = groupingSize;
+ }
+ }
+ }
+
+ // Faster fast-path for the "all nines" cases. Mainly like a "fill" operation.
+ private void formatAllNinesCase(char[] digitsBuffer, int startIndex,
+ boolean toBeRounded, int nbGroups,
+ int integralLength, int fractionalLength) {
+ char fillDigit;
+ if (!toBeRounded) fillDigit = '9';
+ else {
+ // checks for needed additional 3 digits group.
+ nbGroups += ((integralLength % 3) == 0) ? 1 : 0;
+
+ digitsBuffer[startIndex++] = '1';
+ fillDigit = '0';
+ }
+
+ // We will fill buffer with relevant digit.
+ int totalLength = integralLength + fractionalLength + nbGroups + 1;
+ Arrays.fill(digitsBuffer, startIndex, (startIndex + totalLength), fillDigit);
+
+ // Sets decimal point char and group markers.
+ lastUsedIndex = startIndex + nbGroups + integralLength;
+ int cursorIndex = lastUsedIndex;
+ digitsBuffer[cursorIndex--] = decimalSeparatorChar;
+ while (cursorIndex > startIndex) {
+ if (nbGroups > 0) {
+ cursorIndex -= 3;
+ digitsBuffer[cursorIndex--] = groupingChar;
+ nbGroups--;
+ }
+ else break;
+ }
+
+ // Removes fractional digits if needed.
+ if (!toBeRounded || isCurrencyFormat) lastUsedIndex += fractionalLength;
+ else lastUsedIndex--; // fractional part absent
+ }
+
+ // Collects integral digits from passed number, setting group chars if needed.
+ // No rounding done here.
+ // Loops downward starting at backwardIndex position (inclusive).
+ private void collectIntegralDigits(int number,
+ char[] digitsBuffer, int backwardIndex) {
+ int index = backwardIndex;
+ int q;
+ int r;
+ while (number > 999) {
+ // Generate 3 digits per iteration.
+ q = number / 1000;
+ r = number - (q << 10) + (q << 4) + (q << 3); // -1024 +16 +8 = 1000.
+ number = q;
+
+ digitsBuffer[index--] = DigitOnes1000[r];
+ digitsBuffer[index--] = DigitTens1000[r];
+ digitsBuffer[index--] = DigitHundreds1000[r];
+ digitsBuffer[index--] = groupingChar;
+ }
+
+ // Collects last 3 or less digits.
+ digitsBuffer[index--] = DigitOnes1000[number];
+ if (number > 9) {
+ if (number > 99) {
+ digitsBuffer[index--] = DigitTens1000[number];
+ digitsBuffer[index] = DigitHundreds1000[number];
+ }
+ else digitsBuffer[index] = DigitTens1000[number];
+ }
+ }
+
+ // Collects the 2 (currency) or 3 fractional digits. No rounding.
+ // Starting at startIndex position inclusive. Updates LastUsedIndex.
+ private void collectFractionalDigits(int number, boolean currency,
+ char[] digitsBuffer, int startIndex) {
+ lastUsedIndex = currency ? (startIndex + 1) : (startIndex + 2);
+ int index = lastUsedIndex;
+
+ char digitOnes, digitTens;
+ digitOnes = DigitOnes1000[number];
+ digitTens = DigitTens1000[number];
+
+ if (currency) {
+ // Currency case. Always collects fractional digits.
+ digitsBuffer[index] = digitOnes;
+ digitsBuffer[index - 1] = digitTens;
+ }
+ else {
+ // Decimal case. Don't collect ending zeros.
+ if (number == 0) index -= 4;
+ else {
+ index = index - 2;
+ digitsBuffer[index] = DigitHundreds1000[number];
+ // Takes care of ending zeros that we don't collect.
+ if (digitOnes == '0') {
+ if (digitTens != '0') {
+ index = index + 1;
+ digitsBuffer[index] = digitTens;
+ }
+ }
+ else {
+ digitsBuffer[++index] = digitTens;
+ digitsBuffer[++index] = digitOnes;
+ }
+ }
+ lastUsedIndex = index;
+ }
+ }
+
+ // Fast path format main entry point method.
+ // Formatted result is put in passed internalChars char buffer.
+ // Passed double d must be positive.
+ private int fastDoubleFormat(double d, boolean negative,
+ char[] internalChars, int startIndex) {
+ // Extracting integral and fractional part.
+ int integralPartAsInt = (int) d;
+ double fractional = d - (double) integralPartAsInt;
+
+ // Collects integral part infos.
+ int integralLength = intStringSize(integralPartAsInt);
+ boolean integralPartIsAllNines = isAllNines;
+ int nbGroups = nbGroupsNeeded;
+
+ // Collects fractional part infos.
+ isAllNines = false;
+ int fractionalPartAsInt = getFracIntegral(fractional, maximumFractionDigits);
+ int fractionalLength = fracStringSize(fractionalPartAsInt);
+ boolean fractionalPartIsAllNines = isAllNines;
+
+ // Should we round up ?
+ boolean toBeRounded =
+ toBeHalfEvenRounded(d, integralPartAsInt, integralLength,
+ fractionalPartAsInt, fractionalLength);
+
+ if (!integralPartIsAllNines ||
+ !(fractionalPartIsAllNines && (fractionalLength == maximumFractionDigits))) {
+ int decimalPointIndex = startIndex + integralLength + nbGroups;
+ int integralLastIndex = decimalPointIndex - 1;
+ int fractionalStartIndex = decimalPointIndex + 1;
+
+ if (!fractionalPartIsAllNines ||
+ (fractionalLength != maximumFractionDigits)) {
+ // Fractional part to be collected completely in all cases.
+ internalChars[decimalPointIndex] = decimalSeparatorChar;
+ if (toBeRounded) fractionalPartAsInt = fractionalPartAsInt + 1;
+ collectFractionalDigits(fractionalPartAsInt, isCurrencyFormat,
+ internalChars, fractionalStartIndex);
+ collectIntegralDigits(integralPartAsInt,
+ internalChars, integralLastIndex);
+ }
+ else {
+ // Fractional is "all_nines".
+ // Either we have currency and frac=99, or decimal and frac=999.
+ // Fractional will end up either in 99[9] or 00[0] depending on rounding.
+ if (toBeRounded) {
+ // Rounding is propagated to integral.
+ integralPartAsInt = integralPartAsInt + 1;
+
+ // Collects fractional part.
+ if (isCurrencyFormat) {
+ // Fills fractional part with ".00" (pattern rule).
+ lastUsedIndex = decimalPointIndex + maximumFractionDigits;
+ internalChars[decimalPointIndex] = decimalSeparatorChar;
+ internalChars[fractionalStartIndex] = '0';
+ internalChars[lastUsedIndex] = '0';
+ }
+ else {
+ // Not currency, just forget fractional part (pattern rule).
+ lastUsedIndex = integralLastIndex;
+ }
+ }
+ else {
+ // No rounding up. Fractional remains all_nines (999 or 99) !
+ internalChars[decimalPointIndex] = decimalSeparatorChar;
+ fillUpTo4Chars(internalChars, fractionalStartIndex, maximumFractionDigits, '9');
+ lastUsedIndex = decimalPointIndex + maximumFractionDigits;
+ }
+
+ // Then collects integral part. Already rounded up if needed.
+ collectIntegralDigits(integralPartAsInt, internalChars, integralLastIndex);
+
+ }
+ }
+ else formatAllNinesCase(internalChars, startIndex,
+ toBeRounded, nbGroups,
+ integralLength, fractionalLength);
+
+ // Takes into account locale for the digits part only.
+ if (zeroDelta != 0) localizeDigits(internalChars, startIndex);
+
+ // Adds suffix part if needed.
+ if (isSuffixPresent) appendSuffix(negative, internalChars);
+
+ return lastUsedIndex;
+ }
+
+ // A fasthpath shortcut of format(double) to be called by NumberFormat only.
+ //
+ // If instance can be applied fast-path and passed double is not NaN or Infinite,
+ // and is in the integer range, we call directly fastDoubleFormat using the
+ // expected container and prefix position.
+ //
+ // Otherwise returns null since fast-path not possible (convention).
+ //
+ // We check positive and negative zero, minimizing the needed tests
+ // for the most frequent case.
+ String fastFormat(double number) {
+ // Should we check fast path status ?
+ if (fastPathCheckNeeded) checkAndSetFastPathStatus();
+
+ if (isFastPath ) {
+ if (!Double.isNaN(number) &&
+ !Double.isInfinite(number)) {
+
+ int lastIndex;
+ char[] container;
+
+ if (number > 0.0) { // 1 test only for positive doubles
+ if (number <= MAX_INT_AS_DOUBLE) {
+ container = positiveFastPathContainer;
+ lastIndex = fastDoubleFormat(number, false,
+ container, positivePrefix.length());
+ return new String(container, 0, lastIndex + 1);
+ }
+ }
+ else if (number < 0.0) { // 2 tests for negative doubles
+ number = -number;
+ if (number <= MAX_INT_AS_DOUBLE) {
+ container = negativeFastPathContainer;
+ lastIndex = fastDoubleFormat(number, true,
+ container, negativePrefix.length());
+ return new String(container, 0, lastIndex + 1);
+ }
+ }
+ else if ( 1/number > 0.0) { // 3 tests for positive zero. Not frequent.
+ container = positiveFastPathContainer;
+ lastIndex = fastDoubleFormat(number, false,
+ container, positivePrefix.length());
+ return new String(container, 0, lastIndex + 1);
+ }
+ else { // 4 tests for negative zero (hopefully very rare)
+ container = negativeFastPathContainer;
+ lastIndex = fastDoubleFormat(number, true,
+ container, negativePrefix.length());
+ return new String(container, 0, lastIndex + 1);
+ }
+ }
+
+ // Fast-path can't be exercized for this passed double !
+ return null;
+ }
+
+ // DecimalFormat instance is not in a fast path state.
+ return null;
+ }
+
+ // End of Fast path formating algorithm for double ====================================================================
+
/**
* Complete the formatting of a finite number. On entry, the digitList must
* be filled in with the correct digits.
*/
private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
@@ -1638,10 +2239,11 @@
public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
try {
// don't allow multiple references
symbols = (DecimalFormatSymbols) newSymbols.clone();
expandAffixes();
+ fastPathCheckNeeded = true;
} catch (Exception foo) {
// should never happen
}
}
@@ -1659,10 +2261,11 @@
*/
public void setPositivePrefix (String newValue) {
positivePrefix = newValue;
posPrefixPattern = null;
positivePrefixFieldPositions = null;
+ fastPathCheckNeeded = true;
}
/**
* Returns the FieldPositions of the fields in the prefix used for
* positive numbers. This is not used if the user has explicitly set
@@ -1696,10 +2299,11 @@
* <P>Examples: -123, ($123) (with negative suffix), sFr-123
*/
public void setNegativePrefix (String newValue) {
negativePrefix = newValue;
negPrefixPattern = null;
+ fastPathCheckNeeded = true;
}
/**
* Returns the FieldPositions of the fields in the prefix used for
* negative numbers. This is not used if the user has explicitly set
@@ -1733,10 +2337,11 @@
* <P>Example: 123%
*/
public void setPositiveSuffix (String newValue) {
positiveSuffix = newValue;
posSuffixPattern = null;
+ fastPathCheckNeeded = true;
}
/**
* Returns the FieldPositions of the fields in the suffix used for
* positive numbers. This is not used if the user has explicitly set
@@ -1770,10 +2375,11 @@
* <P>Examples: 123%
*/
public void setNegativeSuffix (String newValue) {
negativeSuffix = newValue;
negSuffixPattern = null;
+ fastPathCheckNeeded = true;
}
/**
* Returns the FieldPositions of the fields in the suffix used for
* negative numbers. This is not used if the user has explicitly set
@@ -1819,13 +2425,23 @@
*/
public void setMultiplier (int newValue) {
multiplier = newValue;
bigDecimalMultiplier = null;
bigIntegerMultiplier = null;
+ fastPathCheckNeeded = true;
}
/**
+ * Set whether or not grouping will be used in this format.
+ * @see #isGroupingUsed
+ */
+ public void setGroupingUsed(boolean newValue) {
+ super.setGroupingUsed(newValue);
+ fastPathCheckNeeded = true;
+ }
+
+ /**
* Return the grouping size. Grouping size is the number of digits between
* grouping separators in the integer portion of a number. For example,
* in the number "123,456.78", the grouping size is 3.
* @see #setGroupingSize
* @see java.text.NumberFormat#isGroupingUsed
@@ -1845,10 +2461,11 @@
* @see java.text.NumberFormat#setGroupingUsed
* @see java.text.DecimalFormatSymbols#setGroupingSeparator
*/
public void setGroupingSize (int newValue) {
groupingSize = (byte)newValue;
+ fastPathCheckNeeded = true;
}
/**
* Allows you to get the behavior of the decimal separator with integers.
* (The decimal separator will always appear with decimals.)
@@ -1863,10 +2480,11 @@
* (The decimal separator will always appear with decimals.)
* <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
*/
public void setDecimalSeparatorAlwaysShown(boolean newValue) {
decimalSeparatorAlwaysShown = newValue;
+ fastPathCheckNeeded = true;
}
/**
* Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
* method returns <code>BigDecimal</code>. The default value is false.
@@ -2675,10 +3293,11 @@
if (minimumIntegerDigits > maximumIntegerDigits) {
minimumIntegerDigits = maximumIntegerDigits;
super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
}
+ fastPathCheckNeeded = true;
}
/**
* Sets the minimum number of digits allowed in the integer portion of a
* number.
@@ -2694,10 +3313,11 @@
if (minimumIntegerDigits > maximumIntegerDigits) {
maximumIntegerDigits = minimumIntegerDigits;
super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
}
+ fastPathCheckNeeded = true;
}
/**
* Sets the maximum number of digits allowed in the fraction portion of a
* number.
@@ -2713,10 +3333,11 @@
if (minimumFractionDigits > maximumFractionDigits) {
minimumFractionDigits = maximumFractionDigits;
super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
}
+ fastPathCheckNeeded = true;
}
/**
* Sets the minimum number of digits allowed in the fraction portion of a
* number.
@@ -2732,10 +3353,11 @@
if (minimumFractionDigits > maximumFractionDigits) {
maximumFractionDigits = minimumFractionDigits;
super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
}
+ fastPathCheckNeeded = true;
}
/**
* Gets the maximum number of digits allowed in the integer portion of a
* number.
@@ -2815,10 +3437,11 @@
symbols.setCurrency(currency);
if (isCurrencyFormat) {
expandAffixes();
}
}
+ fastPathCheckNeeded = true;
}
/**
* Gets the {@link java.math.RoundingMode} used in this DecimalFormat.
*
@@ -2843,10 +3466,11 @@
throw new NullPointerException();
}
this.roundingMode = roundingMode;
digitList.setRoundingMode(roundingMode);
+ fastPathCheckNeeded = true;
}
/**
* Adjusts the minimum and maximum fraction digits to values that
* are reasonable for the currency's default fraction digits.
@@ -3194,10 +3818,56 @@
* @serial
* @since 1.6
*/
private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
+ // ------ DecimalFormat fields for Fast Path for double algorithm ------
+
+ // --- Utility temporary fields only used in fast-path algorithms and shared by several methods.
+
+ /* Flag field for "all nine" integers like 99999999 (see StringSize). */
+ private boolean isAllNines = false;
+
+ /* The fractional part of double that is past 2 (currency) or 3 fractional digits. */
+ private double remainingFractionalPart = 0.0d;
+
+ /* The last valid index in the buffer containing the formated result. */
+ private int lastUsedIndex = 0;
+
+ /* Number of groups needed in formatting the double (see intStringSize and fastDoubleFormat). */
+ private int nbGroupsNeeded = 0;
+
+ // --- New state fields of DecimalFormat, necessary for fast-path.
+
+ /* The format-fast-path status of the instance. */
+ private boolean isFastPath = false;
+
+ /* Flag stating need of check fast path status on next format call. */
+ private boolean fastPathCheckNeeded = true;
+
+ /* Preinstantiated digits buffer templates for perf purpose (see checkAndSetFastPathStatus). */
+ private char[] positiveFastPathContainer;
+ private char[] negativeFastPathContainer;
+
+ /* Rounding bounds assigned at initialization (see checkAndSetFastPathStatus). */
+ private double[] safeMinusRoundingBounds = null;
+ private double[] safeMajorRoundingBounds = null;
+
+ /* Suffixes recorded as char array (see checkAndSetFastPathStatus). */
+ private boolean isSuffixPresent;
+ private char[] charsPositiveSuffix;
+ private char[] charsNegativeSuffix;
+
+ /* Difference between locale zero and default zero representation (see checkAndSetFastPathStatus). */
+ private int zeroDelta;
+
+ /* Locale char for grouping separator (see checkAndSetFastPathStatus). */
+ private char groupingChar;
+
+ /* Locale char for decimal separator (see checkAndSetFastPathStatus). */
+ private char decimalSeparatorChar;
+
//----------------------------------------------------------------------
static final int currentSerialVersion = 4;
/**
@@ -3227,10 +3897,119 @@
//----------------------------------------------------------------------
// CONSTANTS
//----------------------------------------------------------------------
+ // ------ The Fast Path for double Constants ------
+
+ /* New DecimalFormat static constant */
+ private static double MAX_INT_AS_DOUBLE = (double) Integer.MAX_VALUE;
+
+ /* Digit arrays for collecting digits. */
+ private static final char[] DigitOnes1000 = new char[1000];
+ private static final char[] DigitTens1000 = new char[1000];
+ private static final char[] DigitHundreds1000 = new char[1000];
+
+ static {
+ int tenIndex = 0;
+ int hundredIndex = 0;
+ char digitOne = '0';
+ char digitTen = '0';
+ char digitHundred = '0';
+ for (int i = 0; i < 1000; i++ ) {
+
+ DigitOnes1000[i] = digitOne;
+ if (digitOne == '9') digitOne = '0';
+ else digitOne++;
+
+ DigitTens1000[i] = digitTen;
+ if (i == (tenIndex + 9)) {
+ tenIndex += 10;
+ if (digitTen == '9') digitTen = '0';
+ else digitTen++;
+ }
+
+ DigitHundreds1000[i] = digitHundred;
+ if (i == (hundredIndex + 99)) {
+ digitHundred++;
+ hundredIndex += 100;
+ }
+ }
+
+ }
+
+ // --- Constant double arrays defining "Undecidable Rounding Interval"
+ private static final double[] StdSafeMajorRoundingBounds = {
+ 0.5000000000000005, // < 0.001d
+ 0.5000000000000005, // < 0.01d
+ 0.50000000000002, // < 0.1d
+ 0.50000000000006, // < 1.0d
+ 0.5000000000010, // < 10.0d
+ 0.500000000008, // < 100.0d
+ 0.50000000006, // < 1000.0d
+ 0.5000000010, // < 10000.0d
+ 0.500000008, // < 100000.0d
+ 0.50000006, // < 1000000.0d
+ 0.5000010, // < 10000000.0d
+ 0.500008, // < 100000000.0d
+ 0.50006, // < 1000000000.0d
+ 0.5002 // < (2**31) - 1
+ };
+
+ // SafeMinusRoundingBounds are set as counterpart of SafeMajorRoundingBounds :
+ // if 0.50000006 for major, then we set to 0.49999994 for minus.
+ private static final double[] StdSafeMinusRoundingBounds = {
+ 0.4999999999999995, // < 0.001d
+ 0.4999999999999995, // < 0.01d
+ 0.49999999999998, // < 0.1d
+ 0.49999999999994, // < 1.0d
+ 0.4999999999990, // < 10.0d
+ 0.499999999992, // < 100.0d
+ 0.49999999994, // < 1000.0d
+ 0.4999999990, // < 10000.0d
+ 0.499999992, // < 100000.0d
+ 0.49999994, // < 1000000.0d
+ 0.4999990, // < 10000000.0d
+ 0.499992, // < 100000000.0d
+ 0.49994, // < 1000000000.0d
+ 0.4998 // < (2**31) - 1
+ };
+
+ private static final double[] CurrencySafeMajorRoundingBounds = {
+ 0.50000000000000009, // < 0.01d
+ 0.5000000000000005, // < 0.1d
+ 0.500000000000008, // < 1.0d
+ 0.50000000000009, // < 10.0d
+ 0.5000000000007, // < 100.0d
+ 0.500000000006, // < 1000.0d
+ 0.50000000009, // < 10000.0d
+ 0.5000000007, // < 100000.0d
+ 0.500000006, // < 1000000.0d
+ 0.50000009, // < 10000000.0d
+ 0.5000008, // < 100000000.0d
+ 0.500006, // < 1000000000.0d
+ 0.50002 // < (2**31) - 1
+ };
+
+ // CurrencySafeMinusRoundingBounds are set as counterpart of CurrencySafeMajorRoundingBounds :
+ // if 0.50000009 for major, then we set to 0.49999991 for minus. This is because program calculating
+ private static final double[] CurrencySafeMinusRoundingBounds = {
+ 0.49999999999999991, // < 0.01d
+ 0.4999999999999995, // < 0.1d
+ 0.499999999999992, // < 1.0d
+ 0.49999999999991, // < 10.0d
+ 0.4999999999993, // < 100.0d
+ 0.499999999994, // < 1000.0d
+ 0.49999999991, // < 10000.0d
+ 0.4999999993, // < 100000.0d
+ 0.499999994, // < 1000000.0d
+ 0.49999991, // < 10000000.0d
+ 0.4999992, // < 100000000.0d
+ 0.499994, // < 1000000000.0d
+ 0.49998 // < (2**31) - 1
+ };
+
// Constants for characters used in programmatic (unlocalized) patterns.
private static final char PATTERN_ZERO_DIGIT = '0';
private static final char PATTERN_GROUPING_SEPARATOR = ',';
private static final char PATTERN_DECIMAL_SEPARATOR = '.';
private static final char PATTERN_PER_MILLE = '\u2030';