| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.text.numbers; |
| |
| /** |
| * Internal class representing a decimal value parsed into separate components. Each number |
| * is represented with |
| * <ul> |
| * <li>a boolean flag for the sign,</li> |
| * <li> a sequence of the digits {@code 0 - 10} representing an unsigned integer with leading and trailing zeros |
| * removed, and</li> |
| * <li>an exponent value that when applied to the base 10 digits produces a floating point value with the |
| * correct magnitude.</li> |
| * </ul> |
| * <p><strong>Examples</strong></p> |
| * <table> |
| * <tr><th>Double</th><th>Negative</th><th>Digits</th><th>Exponent</th></tr> |
| * <tr><td>0.0</td><td>false</td><td>[0]</td><td>0</td></tr> |
| * <tr><td>1.2</td><td>false</td><td>[1, 2]</td><td>-1</td></tr> |
| * <tr><td>-0.00971</td><td>true</td><td>[9, 7, 1]</td><td>-5</td></tr> |
| * <tr><td>56300</td><td>true</td><td>[5, 6, 3]</td><td>2</td></tr> |
| * </table> |
| */ |
| final class ParsedDecimal { |
| |
| /** |
| * Interface containing values used during string formatting. |
| */ |
| interface FormatOptions { |
| |
| /** |
| * Get the decimal separator character. |
| * @return decimal separator character |
| */ |
| char getDecimalSeparator(); |
| |
| /** |
| * Get an array containing the localized digit characters 0-9 in that order. |
| * This string <em>must</em> be non-null and have a length of 10. |
| * @return array containing the digit characters 0-9 |
| */ |
| char[] getDigits(); |
| |
| /** |
| * Get the exponent separator as an array of characters. |
| * @return exponent separator as an array of characters |
| */ |
| char[] getExponentSeparatorChars(); |
| |
| /** |
| * Get the character used to separate thousands groupings. |
| * @return character used to separate thousands groupings |
| */ |
| char getGroupingSeparator(); |
| |
| /** |
| * Get the minus sign character. |
| * @return minus sign character |
| */ |
| char getMinusSign(); |
| |
| /** |
| * Return {@code true} if exponent values should always be included in |
| * formatted output, even if the value is zero. |
| * @return {@code true} if exponent values should always be included |
| */ |
| boolean isAlwaysIncludeExponent(); |
| |
| /** |
| * Return {@code true} if thousands should be grouped. |
| * @return {@code true} if thousand should be grouped |
| */ |
| boolean isGroupThousands(); |
| |
| /** |
| * Return {@code true} if fraction placeholders (e.g., {@code ".0"} in {@code "1.0"}) |
| * should be included. |
| * @return {@code true} if fraction placeholders should be included |
| */ |
| boolean isIncludeFractionPlaceholder(); |
| |
| /** |
| * Return {@code true} if the string zero should be prefixed with the minus sign |
| * for negative zero values. |
| * @return {@code true} if the minus zero string should be allowed |
| */ |
| boolean isSignedZero(); |
| } |
| |
| /** Minus sign character. */ |
| private static final char MINUS_CHAR = '-'; |
| |
| /** Decimal separator character. */ |
| private static final char DECIMAL_SEP_CHAR = '.'; |
| |
| /** Exponent character. */ |
| private static final char EXPONENT_CHAR = 'E'; |
| |
| /** Zero digit character. */ |
| private static final char ZERO_CHAR = '0'; |
| |
| /** Number of characters in thousands groupings. */ |
| private static final int THOUSANDS_GROUP_SIZE = 3; |
| |
| /** Radix for decimal numbers. */ |
| private static final int DECIMAL_RADIX = 10; |
| |
| /** Center value used when rounding. */ |
| private static final int ROUND_CENTER = DECIMAL_RADIX / 2; |
| |
| /** Number that exponents in engineering format must be a multiple of. */ |
| private static final int ENG_EXPONENT_MOD = 3; |
| |
| /** |
| * Gets the numeric value of the given digit character. No validation of the |
| * character type is performed. |
| * @param ch digit character |
| * @return numeric value of the digit character, ex: '1' = 1 |
| */ |
| private static int digitValue(final char ch) { |
| return ch - ZERO_CHAR; |
| } |
| |
| /** |
| * Constructs a new instance from the given double value. |
| * @param d double value |
| * @return a new instance containing the parsed components of the given double value |
| * @throws IllegalArgumentException if {@code d} is {@code NaN} or infinite |
| */ |
| public static ParsedDecimal from(final double d) { |
| if (!Double.isFinite(d)) { |
| throw new IllegalArgumentException("Double is not finite"); |
| } |
| |
| // Get the canonical string representation of the double value and parse |
| // it to extract the components of the decimal value. From the documentation |
| // of Double.toString() and the fact that d is finite, we are guaranteed the |
| // following: |
| // - the string will not be empty |
| // - it will contain exactly one decimal point character |
| // - all digit characters are in the ASCII range |
| final char[] strChars = Double.toString(d).toCharArray(); |
| |
| final boolean negative = strChars[0] == MINUS_CHAR; |
| final int digitStartIdx = negative ? 1 : 0; |
| |
| final int[] digits = new int[strChars.length - digitStartIdx - 1]; |
| |
| boolean foundDecimalPoint = false; |
| int digitCount = 0; |
| int significantDigitCount = 0; |
| int decimalPos = 0; |
| |
| int i; |
| for (i = digitStartIdx; i < strChars.length; ++i) { |
| final char ch = strChars[i]; |
| |
| if (ch == DECIMAL_SEP_CHAR) { |
| foundDecimalPoint = true; |
| decimalPos = digitCount; |
| } else if (ch == EXPONENT_CHAR) { |
| // no more mantissa digits |
| break; |
| } else if (ch != ZERO_CHAR || digitCount > 0) { |
| // this is either the first non-zero digit or one after it |
| final int val = digitValue(ch); |
| digits[digitCount++] = val; |
| |
| if (val > 0) { |
| significantDigitCount = digitCount; |
| } |
| } else if (foundDecimalPoint) { |
| // leading zero in a fraction; adjust the decimal position |
| --decimalPos; |
| } |
| } |
| |
| if (digitCount > 0) { |
| // determine the exponent |
| final int explicitExponent = i < strChars.length |
| ? parseExponent(strChars, i + 1) |
| : 0; |
| final int exponent = explicitExponent + decimalPos - significantDigitCount; |
| |
| return new ParsedDecimal(negative, digits, significantDigitCount, exponent); |
| } |
| |
| // no non-zero digits, so value is zero |
| return new ParsedDecimal(negative, new int[] {0}, 1, 0); |
| } |
| |
| /** |
| * Parses a double exponent value from {@code chars}, starting at the {@code start} |
| * index and continuing through the end of the array. |
| * @param chars character array to parse a double exponent value from |
| * @param start start index |
| * @return parsed exponent value |
| */ |
| private static int parseExponent(final char[] chars, final int start) { |
| int i = start; |
| final boolean neg = chars[i] == MINUS_CHAR; |
| if (neg) { |
| ++i; |
| } |
| |
| int exp = 0; |
| for (; i < chars.length; ++i) { |
| exp = (exp * DECIMAL_RADIX) + digitValue(chars[i]); |
| } |
| |
| return neg ? -exp : exp; |
| } |
| |
| /** True if the value is negative. */ |
| final boolean negative; |
| |
| /** Array containing the significant decimal digits for the value. */ |
| final int[] digits; |
| |
| /** Number of digits used in the digits array; not necessarily equal to the length. */ |
| int digitCount; |
| |
| /** Exponent for the value. */ |
| int exponent; |
| |
| /** Output buffer for use in creating string representations. */ |
| private char[] outputChars; |
| |
| /** Output buffer index. */ |
| private int outputIdx; |
| |
| /** |
| * Constructs a new instance from its parts. |
| * @param negative {@code true} if the value is negative |
| * @param digits array containing significant digits |
| * @param digitCount number of digits used from the {@code digits} array |
| * @param exponent exponent value |
| */ |
| private ParsedDecimal(final boolean negative, final int[] digits, final int digitCount, |
| final int exponent) { |
| this.negative = negative; |
| this.digits = digits; |
| this.digitCount = digitCount; |
| this.exponent = exponent; |
| } |
| |
| /** |
| * Appends the given character to the output buffer. |
| * @param ch character to append |
| */ |
| private void append(final char ch) { |
| outputChars[outputIdx++] = ch; |
| } |
| |
| /** |
| * Appends the given character array directly to the output buffer. |
| * @param chars characters to append |
| */ |
| private void append(final char[] chars) { |
| for (final char c : chars) { |
| append(c); |
| } |
| } |
| |
| /** |
| * Appends the fractional component of the number to the current output buffer. |
| * @param zeroCount number of zeros to add after the decimal point and before the |
| * first significant digit |
| * @param startIdx significant digit start index |
| * @param opts format options |
| */ |
| private void appendFraction(final int zeroCount, final int startIdx, final FormatOptions opts) { |
| final char[] localizedDigits = opts.getDigits(); |
| final char localizedZero = localizedDigits[0]; |
| |
| if (startIdx < digitCount) { |
| append(opts.getDecimalSeparator()); |
| |
| // add the zero prefix |
| for (int i = 0; i < zeroCount; ++i) { |
| append(localizedZero); |
| } |
| |
| // add the fraction digits |
| for (int i = startIdx; i < digitCount; ++i) { |
| appendLocalizedDigit(digits[i], localizedDigits); |
| } |
| } else if (opts.isIncludeFractionPlaceholder()) { |
| append(opts.getDecimalSeparator()); |
| append(localizedZero); |
| } |
| } |
| |
| /** |
| * Appends the localized representation of the digit {@code n} to the output buffer. |
| * @param n digit to append |
| * @param digitChars character array containing localized versions of the digits {@code 0-9} |
| * in that order |
| */ |
| private void appendLocalizedDigit(final int n, final char[] digitChars) { |
| append(digitChars[n]); |
| } |
| |
| /** |
| * Appends the whole number portion of this value to the output buffer. No thousands |
| * separators are added. |
| * @param wholeCount total number of digits required to the left of the decimal point |
| * @param opts format options |
| * @return number of digits from {@code digits} appended to the output buffer |
| * @see #appendWholeGrouped(int, FormatOptions) |
| */ |
| private int appendWhole(final int wholeCount, final FormatOptions opts) { |
| if (shouldIncludeMinus(opts)) { |
| append(opts.getMinusSign()); |
| } |
| |
| final char[] localizedDigits = opts.getDigits(); |
| final char localizedZero = localizedDigits[0]; |
| |
| final int significantDigitCount = Math.max(0, Math.min(wholeCount, digitCount)); |
| |
| if (significantDigitCount > 0) { |
| int i; |
| for (i = 0; i < significantDigitCount; ++i) { |
| appendLocalizedDigit(digits[i], localizedDigits); |
| } |
| |
| for (; i < wholeCount; ++i) { |
| append(localizedZero); |
| } |
| } else { |
| append(localizedZero); |
| } |
| |
| return significantDigitCount; |
| } |
| |
| /** |
| * Appends the whole number portion of this value to the output buffer, adding thousands |
| * separators as needed. |
| * @param wholeCount total number of digits required to the right of the decimal point |
| * @param opts format options |
| * @return number of digits from {@code digits} appended to the output buffer |
| * @see #appendWhole(int, FormatOptions) |
| */ |
| private int appendWholeGrouped(final int wholeCount, final FormatOptions opts) { |
| if (shouldIncludeMinus(opts)) { |
| append(opts.getMinusSign()); |
| } |
| |
| final char[] localizedDigits = opts.getDigits(); |
| final char localizedZero = localizedDigits[0]; |
| final char groupingChar = opts.getGroupingSeparator(); |
| |
| final int appendCount = Math.max(0, Math.min(wholeCount, digitCount)); |
| |
| if (appendCount > 0) { |
| int i; |
| int pos = wholeCount; |
| for (i = 0; i < appendCount; ++i, --pos) { |
| appendLocalizedDigit(digits[i], localizedDigits); |
| if (requiresGroupingSeparatorAfterPosition(pos)) { |
| append(groupingChar); |
| } |
| } |
| |
| for (; i < wholeCount; ++i, --pos) { |
| append(localizedZero); |
| if (requiresGroupingSeparatorAfterPosition(pos)) { |
| append(groupingChar); |
| } |
| } |
| } else { |
| append(localizedZero); |
| } |
| |
| return appendCount; |
| } |
| |
| /** |
| * Gets the number of characters required for the digit portion of a string representation of |
| * this value. This excludes any exponent or thousands groupings characters. |
| * @param decimalPos decimal point position relative to the {@code digits} array |
| * @param opts format options |
| * @return number of characters required for the digit portion of a string representation of |
| * this value |
| */ |
| private int getDigitStringSize(final int decimalPos, final FormatOptions opts) { |
| int size = digitCount; |
| if (shouldIncludeMinus(opts)) { |
| ++size; |
| } |
| if (decimalPos < 1) { |
| // no whole component; |
| // add decimal point and leading zeros |
| size += 2 + Math.abs(decimalPos); |
| } else if (decimalPos >= digitCount) { |
| // no fraction component; |
| // add trailing zeros |
| size += decimalPos - digitCount; |
| if (opts.isIncludeFractionPlaceholder()) { |
| size += 2; |
| } |
| } else { |
| // whole and fraction components; |
| // add decimal point |
| size += 1; |
| } |
| |
| return size; |
| } |
| |
| /** |
| * Gets the exponent value. This exponent produces a floating point value with the |
| * correct magnitude when applied to the internal unsigned integer. |
| * @return exponent value |
| */ |
| public int getExponent() { |
| return exponent; |
| } |
| |
| /** |
| * Gets the number of characters required to create a plain format representation |
| * of this value. |
| * @param decimalPos decimal position relative to the {@code digits} array |
| * @param opts format options |
| * @return number of characters in the plain string representation of this value, |
| * created using the given parameters |
| */ |
| private int getPlainStringSize(final int decimalPos, final FormatOptions opts) { |
| int size = getDigitStringSize(decimalPos, opts); |
| |
| // adjust for groupings if needed |
| if (opts.isGroupThousands() && decimalPos > 0) { |
| size += (decimalPos - 1) / THOUSANDS_GROUP_SIZE; |
| } |
| |
| return size; |
| } |
| |
| /** |
| * Get sthe exponent that would be used when representing this number in scientific |
| * notation (i.e., with a single non-zero digit in front of the decimal point). |
| * @return the exponent that would be used when representing this number in scientific |
| * notation |
| */ |
| public int getScientificExponent() { |
| return digitCount + exponent - 1; |
| } |
| |
| /** |
| * Returns {@code true} if this value is equal to zero. The sign field is ignored, |
| * meaning that this method will return {@code true} for both {@code +0} and {@code -0}. |
| * @return {@code true} if the value is equal to zero |
| */ |
| boolean isZero() { |
| return digits[0] == 0; |
| } |
| |
| /** |
| * Ensures that this instance has <em>at most</em> the given number of significant digits |
| * (i.e. precision). If this instance already has a precision less than or equal |
| * to the argument, nothing is done. If the given precision requires a reduction in the number |
| * of digits, then the value is rounded using {@link java.math.RoundingMode#HALF_EVEN half-even rounding}. |
| * @param precision maximum number of significant digits to include |
| */ |
| public void maxPrecision(final int precision) { |
| if (precision > 0 && precision < digitCount) { |
| if (shouldRoundUp(precision)) { |
| roundUp(precision); |
| } else { |
| truncate(precision); |
| } |
| } |
| } |
| |
| /** |
| * Gets the output buffer as a string. |
| * @return output buffer as a string |
| */ |
| private String outputString() { |
| final String str = String.valueOf(outputChars); |
| outputChars = null; |
| return str; |
| } |
| |
| /** |
| * Prepares the output buffer for a string of the given size. |
| * @param size buffer size |
| */ |
| private void prepareOutput(final int size) { |
| outputChars = new char[size]; |
| outputIdx = 0; |
| } |
| |
| /** |
| * Returns {@code true} if a grouping separator should be added after the whole digit |
| * character at the given position. |
| * @param pos whole digit character position, with values starting at 1 and increasing |
| * from right to left. |
| * @return {@code true} if a grouping separator should be added |
| */ |
| private boolean requiresGroupingSeparatorAfterPosition(final int pos) { |
| return pos > 1 && (pos % THOUSANDS_GROUP_SIZE) == 1; |
| } |
| |
| /** |
| * Rounds the instance to the given decimal exponent position using |
| * {@link java.math.RoundingMode#HALF_EVEN half-even rounding}. For example, a value of {@code -2} |
| * will round the instance to the digit at the position 10<sup>-2</sup> (i.e. to the closest multiple of 0.01). |
| * @param roundExponent exponent defining the decimal place to round to |
| */ |
| public void round(final int roundExponent) { |
| if (roundExponent > exponent) { |
| final int max = digitCount + exponent; |
| |
| if (roundExponent < max) { |
| // rounding to a decimal place less than the max; set max precision |
| maxPrecision(max - roundExponent); |
| } else if (roundExponent == max && shouldRoundUp(0)) { |
| // rounding up directly on the max decimal place |
| setSingleDigitValue(1, roundExponent); |
| } else { |
| // change to zero |
| setSingleDigitValue(0, 0); |
| } |
| } |
| } |
| |
| /** |
| * Rounds the value up to the given number of digits. |
| * @param count target number of digits; must be greater than zero and |
| * less than the current number of digits |
| */ |
| private void roundUp(final int count) { |
| int removedDigits = digitCount - count; |
| int i; |
| for (i = count - 1; i >= 0; --i) { |
| final int d = digits[i] + 1; |
| |
| if (d < DECIMAL_RADIX) { |
| // value did not carry over; done adding |
| digits[i] = d; |
| break; |
| } |
| // value carried over; the current position is 0 |
| // which we will ignore by shortening the digit count |
| ++removedDigits; |
| } |
| |
| if (i < 0) { |
| // all values carried over |
| setSingleDigitValue(1, exponent + removedDigits); |
| } else { |
| // values were updated in-place; just need to update the length |
| truncate(digitCount - removedDigits); |
| } |
| } |
| |
| /** |
| * Sets the value of this instance to a single digit with the given exponent. |
| * The sign of the value is retained. |
| * @param digit digit value |
| * @param newExponent new exponent value |
| */ |
| private void setSingleDigitValue(final int digit, final int newExponent) { |
| digits[0] = digit; |
| digitCount = 1; |
| exponent = newExponent; |
| } |
| |
| /** |
| * Returns {@code true} if a formatted string with the given target exponent should include |
| * the exponent field. |
| * @param targetExponent exponent of the formatted result |
| * @param opts format options |
| * @return {@code true} if the formatted string should include the exponent field |
| */ |
| private boolean shouldIncludeExponent(final int targetExponent, final FormatOptions opts) { |
| return targetExponent != 0 || opts.isAlwaysIncludeExponent(); |
| } |
| |
| /** |
| * Returns {@code true} if formatted strings should include the minus sign, considering |
| * the value of this instance and the given format options. |
| * @param opts format options |
| * @return {@code true} if a minus sign should be included in the output |
| */ |
| private boolean shouldIncludeMinus(final FormatOptions opts) { |
| return negative && (opts.isSignedZero() || !isZero()); |
| } |
| |
| /** |
| * Returns {@code true} if a rounding operation for the given number of digits should |
| * round up. |
| * @param count number of digits to round to; must be greater than zero and less |
| * than the current number of digits |
| * @return {@code true} if a rounding operation for the given number of digits should |
| * round up |
| */ |
| private boolean shouldRoundUp(final int count) { |
| // Round up in the following cases: |
| // 1. The digit after the last digit is greater than 5. |
| // 2. The digit after the last digit is 5 and there are additional (non-zero) |
| // digits after it. |
| // 3. The digit after the last digit is 5, there are no additional digits afterward, |
| // and the last digit is odd (half-even rounding). |
| final int digitAfterLast = digits[count]; |
| |
| return digitAfterLast > ROUND_CENTER || (digitAfterLast == ROUND_CENTER |
| && (count < digitCount - 1 || (digits[count - 1] % 2) != 0)); |
| } |
| |
| /** |
| * Returns a string representation of this value in engineering notation. This |
| * is similar to {@link #toScientificString(FormatOptions) scientific notation} |
| * but with the exponent forced to be a multiple of 3, allowing easier alignment with SI prefixes. |
| * <pre> |
| * 0 = "0.0" |
| * 10 = "10.0" |
| * 1e-6 = "1.0E-6" |
| * 1e11 = "100.0E9" |
| * </pre> |
| * @param opts format options |
| * @return value in engineering format |
| */ |
| public String toEngineeringString(final FormatOptions opts) { |
| final int decimalPos = 1 + Math.floorMod(getScientificExponent(), ENG_EXPONENT_MOD); |
| return toScientificString(decimalPos, opts); |
| } |
| |
| /** |
| * Returns a string representation of this value with no exponent field. Ex: |
| * <pre> |
| * 10 = "10.0" |
| * 1e-6 = "0.000001" |
| * 1e11 = "100000000000.0" |
| * </pre> |
| * @param opts format options |
| * @return value in plain format |
| */ |
| public String toPlainString(final FormatOptions opts) { |
| final int decimalPos = digitCount + exponent; |
| final int fractionZeroCount = decimalPos < 1 |
| ? Math.abs(decimalPos) |
| : 0; |
| |
| prepareOutput(getPlainStringSize(decimalPos, opts)); |
| |
| final int fractionStartIdx = opts.isGroupThousands() |
| ? appendWholeGrouped(decimalPos, opts) |
| : appendWhole(decimalPos, opts); |
| |
| appendFraction(fractionZeroCount, fractionStartIdx, opts); |
| |
| return outputString(); |
| } |
| |
| /** |
| * Returns a string representation of this value in scientific notation. Ex: |
| * <pre> |
| * 0 = "0.0" |
| * 10 = "1.0E1" |
| * 1e-6 = "1.0E-6" |
| * 1e11 = "1.0E11" |
| * </pre> |
| * @param opts format options |
| * @return value in scientific format |
| */ |
| public String toScientificString(final FormatOptions opts) { |
| return toScientificString(1, opts); |
| } |
| |
| /** |
| * Returns a string representation of the value in scientific notation using the |
| * given decimal point position. |
| * @param decimalPos decimal position relative to the {@code digits} array; this value |
| * is expected to be greater than 0 |
| * @param opts format options |
| * @return value in scientific format |
| */ |
| private String toScientificString(final int decimalPos, final FormatOptions opts) { |
| final int targetExponent = digitCount + exponent - decimalPos; |
| final int absTargetExponent = Math.abs(targetExponent); |
| final boolean includeExponent = shouldIncludeExponent(targetExponent, opts); |
| final boolean negativeExponent = targetExponent < 0; |
| |
| // determine the size of the full formatted string, including the number of |
| // characters needed for the exponent digits |
| int size = getDigitStringSize(decimalPos, opts); |
| int exponentDigitCount = 0; |
| if (includeExponent) { |
| exponentDigitCount = absTargetExponent > 0 |
| ? (int) Math.floor(Math.log10(absTargetExponent)) + 1 |
| : 1; |
| |
| size += opts.getExponentSeparatorChars().length + exponentDigitCount; |
| if (negativeExponent) { |
| ++size; |
| } |
| } |
| |
| prepareOutput(size); |
| |
| // append the portion before the exponent field |
| final int fractionStartIdx = appendWhole(decimalPos, opts); |
| appendFraction(0, fractionStartIdx, opts); |
| |
| if (includeExponent) { |
| // append the exponent field |
| append(opts.getExponentSeparatorChars()); |
| |
| if (negativeExponent) { |
| append(opts.getMinusSign()); |
| } |
| |
| // append the exponent digits themselves; compute the |
| // string representation directly and add it to the output |
| // buffer to avoid the overhead of Integer.toString() |
| final char[] localizedDigits = opts.getDigits(); |
| int rem = absTargetExponent; |
| for (int i = size - 1; i >= outputIdx; --i) { |
| outputChars[i] = localizedDigits[rem % DECIMAL_RADIX]; |
| rem /= DECIMAL_RADIX; |
| } |
| outputIdx = size; |
| } |
| |
| return outputString(); |
| } |
| |
| /** |
| * Truncates the value to the given number of digits. |
| * @param count number of digits; must be greater than zero and less than |
| * the current number of digits |
| */ |
| private void truncate(final int count) { |
| // trim all trailing zero digits, making sure to leave |
| // at least one digit left |
| int nonZeroCount = count; |
| for (int i = count - 1; |
| i > 0 && digits[i] == 0; |
| --i) { |
| --nonZeroCount; |
| } |
| exponent += digitCount - nonZeroCount; |
| digitCount = nonZeroCount; |
| } |
| } |