src/share/classes/java/text/DecimalFormat.java

Print this page

        

*** 43,52 **** --- 43,53 ---- 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,529 **** --- 518,544 ---- * @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,864 **** --- 870,1465 ---- "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,1647 **** --- 2239,2249 ---- 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,1668 **** --- 2261,2271 ---- */ 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,1705 **** --- 2299,2309 ---- * <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,1742 **** --- 2337,2347 ---- * <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,1779 **** --- 2375,2385 ---- * <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,1831 **** --- 2425,2447 ---- */ 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,1854 **** --- 2461,2471 ---- * @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,1872 **** --- 2480,2490 ---- * (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,2684 **** --- 3293,3303 ---- 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,2703 **** --- 3313,3323 ---- 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,2722 **** --- 3333,3343 ---- 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,2741 **** --- 3353,3363 ---- 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,2824 **** --- 3437,3447 ---- symbols.setCurrency(currency); if (isCurrencyFormat) { expandAffixes(); } } + fastPathCheckNeeded = true; } /** * Gets the {@link java.math.RoundingMode} used in this DecimalFormat. *
*** 2843,2852 **** --- 3466,3476 ---- 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,3203 **** --- 3818,3873 ---- * @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,3236 **** --- 3897,4015 ---- //---------------------------------------------------------------------- // 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';