X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=jars%2Ficu4j-4_2_1-src%2Fsrc%2Fcom%2Fibm%2Ficu%2Ftext%2FDigitList.java;h=217d92e7ef30c462eae067676baa17369c4d75ea;hb=127973afabe0c34015667c599d68bf9453d85652;hp=32831e544dcf78e18907143f55b41740336495c2;hpb=92dfc8b7d39cbc2e55f3c547c0c265bc7ae3af86;p=Dictionary.git diff --git a/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DigitList.java b/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DigitList.java old mode 100755 new mode 100644 index 32831e5..217d92e --- a/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DigitList.java +++ b/jars/icu4j-4_2_1-src/src/com/ibm/icu/text/DigitList.java @@ -1,865 +1,865 @@ -//##header -/* - ******************************************************************************* - * Copyright (C) 1996-2009, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -package com.ibm.icu.text; - -import java.math.BigInteger; - -/** - * DigitList handles the transcoding between numeric values and - * strings of characters. It only represents non-negative numbers. The - * division of labor between DigitList and - * DecimalFormat is that DigitList handles the radix - * 10 representation issues and numeric conversion, including rounding; - * DecimalFormat handles the locale-specific issues such as - * positive and negative representation, digit grouping, decimal point, - * currency, and so on. - * - *

A DigitList is a representation of a finite numeric value. - * DigitList objects do not represent NaN or infinite - * values. A DigitList value can be converted to a - * BigDecimal without loss of precision. Conversion to other - * numeric formats may involve loss of precision, depending on the specific - * value. - * - *

The DigitList representation consists of a string of - * characters, which are the digits radix 10, from '0' to '9'. It also has a - * base 10 exponent associated with it. The value represented by a - * DigitList object can be computed by mulitplying the fraction - * f, where 0 <= f < 1, derived by placing all the digits of - * the list to the right of the decimal point, by 10^exponent. - * - * @see java.util.Locale - * @see java.text.Format - * @see NumberFormat - * @see DecimalFormat - * @see java.text.ChoiceFormat - * @see java.text.MessageFormat - * @version 1.18 08/12/98 - * @author Mark Davis, Alan Liu - * */ -final class DigitList { - /** - * The maximum number of significant digits in an IEEE 754 double, that - * is, in a Java double. This must not be increased, or garbage digits - * will be generated, and should not be decreased, or accuracy will be lost. - */ - public static final int MAX_LONG_DIGITS = 19; // == Long.toString(Long.MAX_VALUE).length() - public static final int DBL_DIG = 17; - - /** - * These data members are intentionally public and can be set directly. - * - * The value represented is given by placing the decimal point before - * digits[decimalAt]. If decimalAt is < 0, then leading zeros between - * the decimal point and the first nonzero digit are implied. If decimalAt - * is > count, then trailing zeros between the digits[count-1] and the - * decimal point are implied. - * - * Equivalently, the represented value is given by f * 10^decimalAt. Here - * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to - * the right of the decimal. - * - * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We - * don't allow denormalized numbers because our exponent is effectively of - * unlimited magnitude. The count value contains the number of significant - * digits present in digits[]. - * - * Zero is represented by any DigitList with count == 0 or with each digits[i] - * for all i <= count == '0'. - */ - public int decimalAt = 0; - public int count = 0; - public byte[] digits = new byte[MAX_LONG_DIGITS]; - - private final void ensureCapacity(int digitCapacity, int digitsToCopy) { - if (digitCapacity > digits.length) { - byte[] newDigits = new byte[digitCapacity * 2]; - System.arraycopy(digits, 0, newDigits, 0, digitsToCopy); - digits = newDigits; - } - } - - /** - * Return true if the represented number is zero. - */ - boolean isZero() - { - for (int i=0; iBigInteger representing the value stored in this - * DigitList. This method assumes that this object contains - * an integral value; if not, it will return an incorrect value. - * [bnf] - * @param isPositive determines the sign of the returned result - * @return the value of this object as a BigInteger - */ - public BigInteger getBigInteger(boolean isPositive) { - if (isZero()) return BigInteger.valueOf(0); - if (false) { - StringBuffer stringRep = new StringBuffer(count); - if (!isPositive) { - stringRep.append('-'); - } - for (int i=0; i count) { - stringRep.append('0'); - } - return new BigInteger(stringRep.toString()); - } else { - int len = decimalAt > count ? decimalAt : count; - if (!isPositive) { - len += 1; - } - char[] text = new char[len]; - int n = 0; - if (!isPositive) { - text[0] = '-'; - for (int i = 0; i < count; ++i) { - text[i+1] = (char)digits[i]; - } - n = count+1; - } else { - for (int i = 0; i < count; ++i) { - text[i] = (char)digits[i]; - } - n = count; - } - for (int i = n; i < text.length; ++i) { - text[i] = '0'; - } - return new BigInteger(new String(text)); - } - } - - private String getStringRep(boolean isPositive) { - if (isZero()) return "0"; - StringBuffer stringRep = new StringBuffer(count+1); - if (!isPositive) { - stringRep.append('-'); - } - int d = decimalAt; - if (d < 0) { - stringRep.append('.'); - while (d < 0) { - stringRep.append('0'); - ++d; - } - d = -1; - } - for (int i=0; i count) { - stringRep.append('0'); - } - return stringRep.toString(); - } - -//#if defined(FOUNDATION10) -//#else - /** - * Return a BigDecimal representing the value stored in this - * DigitList. - * [bnf] - * @param isPositive determines the sign of the returned result - * @return the value of this object as a BigDecimal - */ - public java.math.BigDecimal getBigDecimal(boolean isPositive) { - if (isZero()) { - return java.math.BigDecimal.valueOf(0); - } - // if exponential notion is negative, - // we prefer to use BigDecimal constructor with scale, - // because it works better when extremely small value - // is used. See #5698. - long scale = (long)count - (long)decimalAt; - if (scale > 0) { - int numDigits = count; - if (scale > (long)Integer.MAX_VALUE) { - // try to reduce the scale - long numShift = scale - (long)Integer.MAX_VALUE; - if (numShift < count) { - numDigits -= numShift; - } else { - // fallback to 0 - return new java.math.BigDecimal(0); - } - } - StringBuffer significantDigits = new StringBuffer(numDigits + 1); - if (!isPositive) { - significantDigits.append('-'); - } - for (int i = 0; i < numDigits; i++) { - significantDigits.append((char)digits[i]); - } - BigInteger unscaledVal = new BigInteger(significantDigits.toString()); - return new java.math.BigDecimal(unscaledVal, (int)scale); - } else { - // We should be able to use a negative scale value for a positive exponential - // value on JDK1.5. But it is not supported by older JDK. So, for now, - // we always use BigDecimal constructor which takes String. - return new java.math.BigDecimal(getStringRep(isPositive)); - } - } -//#endif - - /** - * Return an ICU BigDecimal representing the value stored in this - * DigitList. - * [bnf] - * @param isPositive determines the sign of the returned result - * @return the value of this object as a BigDecimal - */ - public com.ibm.icu.math.BigDecimal getBigDecimalICU(boolean isPositive) { - if (isZero()) { - return com.ibm.icu.math.BigDecimal.valueOf(0); - } - // if exponential notion is negative, - // we prefer to use BigDecimal constructor with scale, - // because it works better when extremely small value - // is used. See #5698. - long scale = (long)count - (long)decimalAt; - if (scale > 0) { - int numDigits = count; - if (scale > (long)Integer.MAX_VALUE) { - // try to reduce the scale - long numShift = scale - (long)Integer.MAX_VALUE; - if (numShift < count) { - numDigits -= numShift; - } else { - // fallback to 0 - return new com.ibm.icu.math.BigDecimal(0); - } - } - StringBuffer significantDigits = new StringBuffer(numDigits + 1); - if (!isPositive) { - significantDigits.append('-'); - } - for (int i = 0; i < numDigits; i++) { - significantDigits.append((char)digits[i]); - } - BigInteger unscaledVal = new BigInteger(significantDigits.toString()); - return new com.ibm.icu.math.BigDecimal(unscaledVal, (int)scale); - } else { - return new com.ibm.icu.math.BigDecimal(getStringRep(isPositive)); - } - } - - /** - * Return whether or not this objects represented value is an integer. - * [bnf] - * @return true if the represented value of this object is an integer - */ - boolean isIntegral() { - // Trim trailing zeros. This does not change the represented value. - while (count > 0 && digits[count - 1] == (byte)'0') --count; - return count == 0 || decimalAt >= count; - } - -// Unused as of ICU 2.6 - alan -// /** -// * Return true if the number represented by this object can fit into -// * a long. -// */ -// boolean fitsIntoLong(boolean isPositive) -// { -// // Figure out if the result will fit in a long. We have to -// // first look for nonzero digits after the decimal point; -// // then check the size. If the digit count is 18 or less, then -// // the value can definitely be represented as a long. If it is 19 -// // then it may be too large. -// -// // Trim trailing zeros. This does not change the represented value. -// while (count > 0 && digits[count - 1] == (byte)'0') --count; -// -// if (count == 0) { -// // Positive zero fits into a long, but negative zero can only -// // be represented as a double. - bug 4162852 -// return isPositive; -// } -// -// if (decimalAt < count || decimalAt > MAX_LONG_DIGITS) return false; -// -// if (decimalAt < MAX_LONG_DIGITS) return true; -// -// // At this point we have decimalAt == count, and count == MAX_LONG_DIGITS. -// // The number will overflow if it is larger than 9223372036854775807 -// // or smaller than -9223372036854775808. -// for (int i=0; i max) return false; -// if (dig < max) return true; -// } -// -// // At this point the first count digits match. If decimalAt is less -// // than count, then the remaining digits are zero, and we return true. -// if (count < decimalAt) return true; -// -// // Now we have a representation of Long.MIN_VALUE, without the leading -// // negative sign. If this represents a positive value, then it does -// // not fit; otherwise it fits. -// return !isPositive; -// } - -// Unused as of ICU 2.6 - alan -// /** -// * Set the digit list to a representation of the given double value. -// * This method supports fixed-point notation. -// * @param source Value to be converted; must not be Inf, -Inf, Nan, -// * or a value <= 0. -// * @param maximumFractionDigits The most fractional digits which should -// * be converted. -// */ -// public final void set(double source, int maximumFractionDigits) -// { -// set(source, maximumFractionDigits, true); -// } - - /** - * Set the digit list to a representation of the given double value. - * This method supports both fixed-point and exponential notation. - * @param source Value to be converted; must not be Inf, -Inf, Nan, - * or a value <= 0. - * @param maximumDigits The most fractional or total digits which should - * be converted. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - final void set(double source, int maximumDigits, boolean fixedPoint) - { - if (source == 0) source = 0; - // Generate a representation of the form DDDDD, DDDDD.DDDDD, or - // DDDDDE+/-DDDDD. - String rep = Double.toString(source); - - set(rep, MAX_LONG_DIGITS); - - if (fixedPoint) { - // The negative of the exponent represents the number of leading - // zeros between the decimal and the first non-zero digit, for - // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this - // is more than the maximum fraction digits, then we have an underflow - // for the printed representation. - if (-decimalAt > maximumDigits) { - count = 0; - return; - } else if (-decimalAt == maximumDigits) { - if (shouldRoundUp(0)) { - count = 1; - ++decimalAt; - digits[0] = (byte)'1'; - } else { - count = 0; - } - return; - } - // else fall through - } - - // Eliminate trailing zeros. - while (count > 1 && digits[count - 1] == '0') - --count; - - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); - } - - /** - * Given a string representation of the form DDDDD, DDDDD.DDDDD, - * or DDDDDE+/-DDDDD, set this object's value to it. Ignore - * any leading '-'. - */ - private void set(String rep, int maxCount) { - decimalAt = -1; - count = 0; - int exponent = 0; - // Number of zeros between decimal point and first non-zero digit after - // decimal point, for numbers < 1. - int leadingZerosAfterDecimal = 0; - boolean nonZeroDigitSeen = false; - // Skip over leading '-' - int i=0; - if (rep.charAt(i) == '-') { - ++i; - } - for (; i < rep.length(); ++i) { - char c = rep.charAt(i); - if (c == '.') { - decimalAt = count; - } else if (c == 'e' || c == 'E') { - ++i; - // Integer.parseInt doesn't handle leading '+' signs - if (rep.charAt(i) == '+') { - ++i; - } - exponent = Integer.valueOf(rep.substring(i)).intValue(); - break; - } else if (count < maxCount) { - if (!nonZeroDigitSeen) { - nonZeroDigitSeen = (c != '0'); - if (!nonZeroDigitSeen && decimalAt != -1) { - ++leadingZerosAfterDecimal; - } - } - - if (nonZeroDigitSeen) { - ensureCapacity(count+1, count); - digits[count++] = (byte)c; - } - } - } - if (decimalAt == -1) { - decimalAt = count; - } - decimalAt += exponent - leadingZerosAfterDecimal; - } - - /** - * Return true if truncating the representation to the given number - * of digits will result in an increment to the last digit. This - * method implements half-even rounding, the default rounding mode. - * [bnf] - * @param maximumDigits the number of digits to keep, from 0 to - * count-1. If 0, then all digits are rounded away, and - * this method returns true if a one should be generated (e.g., formatting - * 0.09 with "#.#"). - * @return true if digit maximumDigits-1 should be - * incremented - */ - private boolean shouldRoundUp(int maximumDigits) { - // variable not used boolean increment = false; - // Implement IEEE half-even rounding - /*Bug 4243108 - format(0.0) gives "0.1" if preceded by parse("99.99") [Richard/GCL] - */ - if (maximumDigits < count) { - if (digits[maximumDigits] > '5') { - return true; - } else if (digits[maximumDigits] == '5' ) { - for (int i=maximumDigits+1; i 0 && (digits[maximumDigits-1] % 2 != 0); - } - } - return false; - } - - /** - * Round the representation to the given number of digits. - * @param maximumDigits The maximum number of digits to be shown. - * Upon return, count will be less than or equal to maximumDigits. - * This now performs rounding when maximumDigits is 0, formerly it did not. - */ - public final void round(int maximumDigits) { - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - // [bnf] rewritten to fix 4179818 - if (maximumDigits >= 0 && maximumDigits < count) { - if (shouldRoundUp(maximumDigits)) { - // Rounding up involves incrementing digits from LSD to MSD. - // In most cases this is simple, but in a worst case situation - // (9999..99) we have to adjust the decimalAt value. - for (;;) - { - --maximumDigits; - if (maximumDigits < 0) - { - // We have all 9's, so we increment to a single digit - // of one and adjust the exponent. - digits[0] = (byte) '1'; - ++decimalAt; - maximumDigits = 0; // Adjust the count - break; - } - - ++digits[maximumDigits]; - if (digits[maximumDigits] <= '9') break; - // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this - } - ++maximumDigits; // Increment for use as count - } - count = maximumDigits; - /*Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1" - Eliminate trailing zeros. [Richard/GCL] - */ - while (count > 1 && digits[count-1] == '0') { - --count; - } //[Richard/GCL] - } - } - - /** - * Utility routine to set the value of the digit list from a long - */ - public final void set(long source) - { - set(source, 0); - } - - /** - * Set the digit list to a representation of the given long value. - * @param source Value to be converted; must be >= 0 or == - * Long.MIN_VALUE. - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - */ - public final void set(long source, int maximumDigits) - { - // This method does not expect a negative number. However, - // "source" can be a Long.MIN_VALUE (-9223372036854775808), - // if the number being formatted is a Long.MIN_VALUE. In that - // case, it will be formatted as -Long.MIN_VALUE, a number - // which is outside the legal range of a long, but which can - // be represented by DigitList. - // [NEW] Faster implementation - if (source <= 0) { - if (source == Long.MIN_VALUE) { - decimalAt = count = MAX_LONG_DIGITS; - System.arraycopy(LONG_MIN_REP, 0, digits, 0, count); - } else { - count = 0; - decimalAt = 0; - } - } else { - int left = MAX_LONG_DIGITS; - int right; - while (source > 0) { - digits[--left] = (byte) (((long) '0') + (source % 10)); - source /= 10; - } - decimalAt = MAX_LONG_DIGITS-left; - // Don't copy trailing zeros - // we are guaranteed that there is at least one non-zero digit, - // so we don't have to check lower bounds - for (right = MAX_LONG_DIGITS - 1; digits[right] == (byte) '0'; --right) {} - count = right - left + 1; - System.arraycopy(digits, left, digits, 0, count); - } - if (maximumDigits > 0) round(maximumDigits); - } - - /** - * Set the digit list to a representation of the given BigInteger value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - */ - public final void set(BigInteger source, int maximumDigits) { - String stringDigits = source.toString(); - - count = decimalAt = stringDigits.length(); - - // Don't copy trailing zeros - while (count > 1 && stringDigits.charAt(count - 1) == '0') --count; - - int offset = 0; - if (stringDigits.charAt(0) == '-') { - ++offset; - --count; - --decimalAt; - } - - ensureCapacity(count, 0); - for (int i = 0; i < count; ++i) { - digits[i] = (byte) stringDigits.charAt(i + offset); - } - - if (maximumDigits > 0) round(maximumDigits); - } - - /** - * Internal method that sets this digit list to represent the - * given value. The value is given as a String of the format - * returned by BigDecimal. - * @param stringDigits value to be represented with the following - * syntax, expressed as a regular expression: -?\d*.?\d* - * Must not be an empty string. - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - private void setBigDecimalDigits(String stringDigits, - int maximumDigits, boolean fixedPoint) { -//| // Find the first non-zero digit, the decimal, and the last non-zero digit. -//| int first=-1, last=stringDigits.length()-1, decimal=-1; -//| for (int i=0; (first<0 || decimal<0) && i<=last; ++i) { -//| char c = stringDigits.charAt(i); -//| if (c == '.') { -//| decimal = i; -//| } else if (first < 0 && (c >= '1' && c <= '9')) { -//| first = i; -//| } -//| } -//| -//| if (first < 0) { -//| clear(); -//| return; -//| } -//| -//| // At this point we know there is at least one non-zero digit, so the -//| // following loop is safe. -//| for (;;) { -//| char c = stringDigits.charAt(last); -//| if (c != '0' && c != '.') { -//| break; -//| } -//| --last; -//| } -//| -//| if (decimal < 0) { -//| decimal = stringDigits.length(); -//| } -//| -//| count = last - first; -//| if (decimal < first || decimal > last) { -//| ++count; -//| } -//| decimalAt = decimal - first; -//| if (decimalAt < 0) { -//| ++decimalAt; -//| } -//| -//| ensureCapacity(count, 0); -//| for (int i = 0; i < count; ++i) { -//| digits[i] = (byte) stringDigits.charAt(first++); -//| if (first == decimal) { -//| ++first; -//| } -//| } - - // The maxDigits here could also be Integer.MAX_VALUE - set(stringDigits, stringDigits.length()); - - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - // {dlf} Some callers depend on passing '0' to round to mean 'don't round', but - // rather than pass that information explicitly, we rely on some magic with maximumDigits - // and decimalAt. Unfortunately, this is no good, because there are cases where maximumDigits - // is zero and we do want to round, e.g. BigDecimal values -1 < x < 1. So since round - // changed to perform rounding when the argument is 0, we now force the argument - // to -1 in the situations where it matters. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); - } - -//#if defined(FOUNDATION10) -//#else - /** - * Set the digit list to a representation of the given BigDecimal value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - public final void set(java.math.BigDecimal source, - int maximumDigits, boolean fixedPoint) { - setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); - } -//#endif - - /* - * Set the digit list to a representation of the given BigDecimal value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - public final void set(com.ibm.icu.math.BigDecimal source, - int maximumDigits, boolean fixedPoint) { - setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); - } - - /** - * Returns true if this DigitList represents Long.MIN_VALUE; - * false, otherwise. This is required so that getLong() works. - */ - private boolean isLongMIN_VALUE() - { - if (decimalAt != count || count != MAX_LONG_DIGITS) - return false; - - for (int i = 0; i < count; ++i) - { - if (digits[i] != LONG_MIN_REP[i]) return false; - } - - return true; - } - - private static byte[] LONG_MIN_REP; - - static - { - // Store the representation of LONG_MIN without the leading '-' - String s = Long.toString(Long.MIN_VALUE); - LONG_MIN_REP = new byte[MAX_LONG_DIGITS]; - for (int i=0; i < MAX_LONG_DIGITS; ++i) - { - LONG_MIN_REP[i] = (byte)s.charAt(i + 1); - } - } - -// Unused -- Alan 2003-05 -// /** -// * Return the floor of the log base 10 of a given double. -// * This method compensates for inaccuracies which arise naturally when -// * computing logs, and always give the correct value. The parameter -// * must be positive and finite. -// */ -// private static final int log10(double d) -// { -// // The reason this routine is needed is that simply taking the -// // log and dividing by log10 yields a result which may be off -// // by 1 due to rounding errors. For example, the naive log10 -// // of 1.0e300 taken this way is 299, rather than 300. -// double log10 = Math.log(d) / LOG10; -// int ilog10 = (int)Math.floor(log10); -// // Positive logs could be too small, e.g. 0.99 instead of 1.0 -// if (log10 > 0 && d >= Math.pow(10, ilog10 + 1)) -// { -// ++ilog10; -// } -// // Negative logs could be too big, e.g. -0.99 instead of -1.0 -// else if (log10 < 0 && d < Math.pow(10, ilog10)) -// { -// --ilog10; -// } -// return ilog10; -// } -// -// private static final double LOG10 = Math.log(10.0); - - // (The following boilerplate methods are currently not called, - // and cannot be called by tests since this class is - // package-private. The methods may be useful in the future, so - // we do not delete them. 2003-06-11 ICU 2.6 Alan) - ///CLOVER:OFF - /** - * equality test between two digit lists. - */ - public boolean equals(Object obj) { - if (this == obj) // quick check - return true; - if (!(obj instanceof DigitList)) // (1) same object? - return false; - DigitList other = (DigitList) obj; - if (count != other.count || - decimalAt != other.decimalAt) - return false; - for (int i = 0; i < count; i++) - if (digits[i] != other.digits[i]) - return false; - return true; - } - - /** - * Generates the hash code for the digit list. - */ - public int hashCode() { - int hashcode = decimalAt; - - for (int i = 0; i < count; i++) - hashcode = hashcode * 37 + digits[i]; - - return hashcode; - } - - public String toString() - { - if (isZero()) return "0"; - StringBuffer buf = new StringBuffer("0."); - for (int i=0; iDigitList handles the transcoding between numeric values and + * strings of characters. It only represents non-negative numbers. The + * division of labor between DigitList and + * DecimalFormat is that DigitList handles the radix + * 10 representation issues and numeric conversion, including rounding; + * DecimalFormat handles the locale-specific issues such as + * positive and negative representation, digit grouping, decimal point, + * currency, and so on. + * + *

A DigitList is a representation of a finite numeric value. + * DigitList objects do not represent NaN or infinite + * values. A DigitList value can be converted to a + * BigDecimal without loss of precision. Conversion to other + * numeric formats may involve loss of precision, depending on the specific + * value. + * + *

The DigitList representation consists of a string of + * characters, which are the digits radix 10, from '0' to '9'. It also has a + * base 10 exponent associated with it. The value represented by a + * DigitList object can be computed by mulitplying the fraction + * f, where 0 <= f < 1, derived by placing all the digits of + * the list to the right of the decimal point, by 10^exponent. + * + * @see java.util.Locale + * @see java.text.Format + * @see NumberFormat + * @see DecimalFormat + * @see java.text.ChoiceFormat + * @see java.text.MessageFormat + * @version 1.18 08/12/98 + * @author Mark Davis, Alan Liu + * */ +final class DigitList { + /** + * The maximum number of significant digits in an IEEE 754 double, that + * is, in a Java double. This must not be increased, or garbage digits + * will be generated, and should not be decreased, or accuracy will be lost. + */ + public static final int MAX_LONG_DIGITS = 19; // == Long.toString(Long.MAX_VALUE).length() + public static final int DBL_DIG = 17; + + /** + * These data members are intentionally public and can be set directly. + * + * The value represented is given by placing the decimal point before + * digits[decimalAt]. If decimalAt is < 0, then leading zeros between + * the decimal point and the first nonzero digit are implied. If decimalAt + * is > count, then trailing zeros between the digits[count-1] and the + * decimal point are implied. + * + * Equivalently, the represented value is given by f * 10^decimalAt. Here + * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to + * the right of the decimal. + * + * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We + * don't allow denormalized numbers because our exponent is effectively of + * unlimited magnitude. The count value contains the number of significant + * digits present in digits[]. + * + * Zero is represented by any DigitList with count == 0 or with each digits[i] + * for all i <= count == '0'. + */ + public int decimalAt = 0; + public int count = 0; + public byte[] digits = new byte[MAX_LONG_DIGITS]; + + private final void ensureCapacity(int digitCapacity, int digitsToCopy) { + if (digitCapacity > digits.length) { + byte[] newDigits = new byte[digitCapacity * 2]; + System.arraycopy(digits, 0, newDigits, 0, digitsToCopy); + digits = newDigits; + } + } + + /** + * Return true if the represented number is zero. + */ + boolean isZero() + { + for (int i=0; iBigInteger representing the value stored in this + * DigitList. This method assumes that this object contains + * an integral value; if not, it will return an incorrect value. + * [bnf] + * @param isPositive determines the sign of the returned result + * @return the value of this object as a BigInteger + */ + public BigInteger getBigInteger(boolean isPositive) { + if (isZero()) return BigInteger.valueOf(0); + if (false) { + StringBuffer stringRep = new StringBuffer(count); + if (!isPositive) { + stringRep.append('-'); + } + for (int i=0; i count) { + stringRep.append('0'); + } + return new BigInteger(stringRep.toString()); + } else { + int len = decimalAt > count ? decimalAt : count; + if (!isPositive) { + len += 1; + } + char[] text = new char[len]; + int n = 0; + if (!isPositive) { + text[0] = '-'; + for (int i = 0; i < count; ++i) { + text[i+1] = (char)digits[i]; + } + n = count+1; + } else { + for (int i = 0; i < count; ++i) { + text[i] = (char)digits[i]; + } + n = count; + } + for (int i = n; i < text.length; ++i) { + text[i] = '0'; + } + return new BigInteger(new String(text)); + } + } + + private String getStringRep(boolean isPositive) { + if (isZero()) return "0"; + StringBuffer stringRep = new StringBuffer(count+1); + if (!isPositive) { + stringRep.append('-'); + } + int d = decimalAt; + if (d < 0) { + stringRep.append('.'); + while (d < 0) { + stringRep.append('0'); + ++d; + } + d = -1; + } + for (int i=0; i count) { + stringRep.append('0'); + } + return stringRep.toString(); + } + +//#if defined(FOUNDATION10) +//#else + /** + * Return a BigDecimal representing the value stored in this + * DigitList. + * [bnf] + * @param isPositive determines the sign of the returned result + * @return the value of this object as a BigDecimal + */ + public java.math.BigDecimal getBigDecimal(boolean isPositive) { + if (isZero()) { + return java.math.BigDecimal.valueOf(0); + } + // if exponential notion is negative, + // we prefer to use BigDecimal constructor with scale, + // because it works better when extremely small value + // is used. See #5698. + long scale = (long)count - (long)decimalAt; + if (scale > 0) { + int numDigits = count; + if (scale > (long)Integer.MAX_VALUE) { + // try to reduce the scale + long numShift = scale - (long)Integer.MAX_VALUE; + if (numShift < count) { + numDigits -= numShift; + } else { + // fallback to 0 + return new java.math.BigDecimal(0); + } + } + StringBuffer significantDigits = new StringBuffer(numDigits + 1); + if (!isPositive) { + significantDigits.append('-'); + } + for (int i = 0; i < numDigits; i++) { + significantDigits.append((char)digits[i]); + } + BigInteger unscaledVal = new BigInteger(significantDigits.toString()); + return new java.math.BigDecimal(unscaledVal, (int)scale); + } else { + // We should be able to use a negative scale value for a positive exponential + // value on JDK1.5. But it is not supported by older JDK. So, for now, + // we always use BigDecimal constructor which takes String. + return new java.math.BigDecimal(getStringRep(isPositive)); + } + } +//#endif + + /** + * Return an ICU BigDecimal representing the value stored in this + * DigitList. + * [bnf] + * @param isPositive determines the sign of the returned result + * @return the value of this object as a BigDecimal + */ + public com.ibm.icu.math.BigDecimal getBigDecimalICU(boolean isPositive) { + if (isZero()) { + return com.ibm.icu.math.BigDecimal.valueOf(0); + } + // if exponential notion is negative, + // we prefer to use BigDecimal constructor with scale, + // because it works better when extremely small value + // is used. See #5698. + long scale = (long)count - (long)decimalAt; + if (scale > 0) { + int numDigits = count; + if (scale > (long)Integer.MAX_VALUE) { + // try to reduce the scale + long numShift = scale - (long)Integer.MAX_VALUE; + if (numShift < count) { + numDigits -= numShift; + } else { + // fallback to 0 + return new com.ibm.icu.math.BigDecimal(0); + } + } + StringBuffer significantDigits = new StringBuffer(numDigits + 1); + if (!isPositive) { + significantDigits.append('-'); + } + for (int i = 0; i < numDigits; i++) { + significantDigits.append((char)digits[i]); + } + BigInteger unscaledVal = new BigInteger(significantDigits.toString()); + return new com.ibm.icu.math.BigDecimal(unscaledVal, (int)scale); + } else { + return new com.ibm.icu.math.BigDecimal(getStringRep(isPositive)); + } + } + + /** + * Return whether or not this objects represented value is an integer. + * [bnf] + * @return true if the represented value of this object is an integer + */ + boolean isIntegral() { + // Trim trailing zeros. This does not change the represented value. + while (count > 0 && digits[count - 1] == (byte)'0') --count; + return count == 0 || decimalAt >= count; + } + +// Unused as of ICU 2.6 - alan +// /** +// * Return true if the number represented by this object can fit into +// * a long. +// */ +// boolean fitsIntoLong(boolean isPositive) +// { +// // Figure out if the result will fit in a long. We have to +// // first look for nonzero digits after the decimal point; +// // then check the size. If the digit count is 18 or less, then +// // the value can definitely be represented as a long. If it is 19 +// // then it may be too large. +// +// // Trim trailing zeros. This does not change the represented value. +// while (count > 0 && digits[count - 1] == (byte)'0') --count; +// +// if (count == 0) { +// // Positive zero fits into a long, but negative zero can only +// // be represented as a double. - bug 4162852 +// return isPositive; +// } +// +// if (decimalAt < count || decimalAt > MAX_LONG_DIGITS) return false; +// +// if (decimalAt < MAX_LONG_DIGITS) return true; +// +// // At this point we have decimalAt == count, and count == MAX_LONG_DIGITS. +// // The number will overflow if it is larger than 9223372036854775807 +// // or smaller than -9223372036854775808. +// for (int i=0; i max) return false; +// if (dig < max) return true; +// } +// +// // At this point the first count digits match. If decimalAt is less +// // than count, then the remaining digits are zero, and we return true. +// if (count < decimalAt) return true; +// +// // Now we have a representation of Long.MIN_VALUE, without the leading +// // negative sign. If this represents a positive value, then it does +// // not fit; otherwise it fits. +// return !isPositive; +// } + +// Unused as of ICU 2.6 - alan +// /** +// * Set the digit list to a representation of the given double value. +// * This method supports fixed-point notation. +// * @param source Value to be converted; must not be Inf, -Inf, Nan, +// * or a value <= 0. +// * @param maximumFractionDigits The most fractional digits which should +// * be converted. +// */ +// public final void set(double source, int maximumFractionDigits) +// { +// set(source, maximumFractionDigits, true); +// } + + /** + * Set the digit list to a representation of the given double value. + * This method supports both fixed-point and exponential notation. + * @param source Value to be converted; must not be Inf, -Inf, Nan, + * or a value <= 0. + * @param maximumDigits The most fractional or total digits which should + * be converted. + * @param fixedPoint If true, then maximumDigits is the maximum + * fractional digits to be converted. If false, total digits. + */ + final void set(double source, int maximumDigits, boolean fixedPoint) + { + if (source == 0) source = 0; + // Generate a representation of the form DDDDD, DDDDD.DDDDD, or + // DDDDDE+/-DDDDD. + String rep = Double.toString(source); + + set(rep, MAX_LONG_DIGITS); + + if (fixedPoint) { + // The negative of the exponent represents the number of leading + // zeros between the decimal and the first non-zero digit, for + // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this + // is more than the maximum fraction digits, then we have an underflow + // for the printed representation. + if (-decimalAt > maximumDigits) { + count = 0; + return; + } else if (-decimalAt == maximumDigits) { + if (shouldRoundUp(0)) { + count = 1; + ++decimalAt; + digits[0] = (byte)'1'; + } else { + count = 0; + } + return; + } + // else fall through + } + + // Eliminate trailing zeros. + while (count > 1 && digits[count - 1] == '0') + --count; + + // Eliminate digits beyond maximum digits to be displayed. + // Round up if appropriate. + round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); + } + + /** + * Given a string representation of the form DDDDD, DDDDD.DDDDD, + * or DDDDDE+/-DDDDD, set this object's value to it. Ignore + * any leading '-'. + */ + private void set(String rep, int maxCount) { + decimalAt = -1; + count = 0; + int exponent = 0; + // Number of zeros between decimal point and first non-zero digit after + // decimal point, for numbers < 1. + int leadingZerosAfterDecimal = 0; + boolean nonZeroDigitSeen = false; + // Skip over leading '-' + int i=0; + if (rep.charAt(i) == '-') { + ++i; + } + for (; i < rep.length(); ++i) { + char c = rep.charAt(i); + if (c == '.') { + decimalAt = count; + } else if (c == 'e' || c == 'E') { + ++i; + // Integer.parseInt doesn't handle leading '+' signs + if (rep.charAt(i) == '+') { + ++i; + } + exponent = Integer.valueOf(rep.substring(i)).intValue(); + break; + } else if (count < maxCount) { + if (!nonZeroDigitSeen) { + nonZeroDigitSeen = (c != '0'); + if (!nonZeroDigitSeen && decimalAt != -1) { + ++leadingZerosAfterDecimal; + } + } + + if (nonZeroDigitSeen) { + ensureCapacity(count+1, count); + digits[count++] = (byte)c; + } + } + } + if (decimalAt == -1) { + decimalAt = count; + } + decimalAt += exponent - leadingZerosAfterDecimal; + } + + /** + * Return true if truncating the representation to the given number + * of digits will result in an increment to the last digit. This + * method implements half-even rounding, the default rounding mode. + * [bnf] + * @param maximumDigits the number of digits to keep, from 0 to + * count-1. If 0, then all digits are rounded away, and + * this method returns true if a one should be generated (e.g., formatting + * 0.09 with "#.#"). + * @return true if digit maximumDigits-1 should be + * incremented + */ + private boolean shouldRoundUp(int maximumDigits) { + // variable not used boolean increment = false; + // Implement IEEE half-even rounding + /*Bug 4243108 + format(0.0) gives "0.1" if preceded by parse("99.99") [Richard/GCL] + */ + if (maximumDigits < count) { + if (digits[maximumDigits] > '5') { + return true; + } else if (digits[maximumDigits] == '5' ) { + for (int i=maximumDigits+1; i 0 && (digits[maximumDigits-1] % 2 != 0); + } + } + return false; + } + + /** + * Round the representation to the given number of digits. + * @param maximumDigits The maximum number of digits to be shown. + * Upon return, count will be less than or equal to maximumDigits. + * This now performs rounding when maximumDigits is 0, formerly it did not. + */ + public final void round(int maximumDigits) { + // Eliminate digits beyond maximum digits to be displayed. + // Round up if appropriate. + // [bnf] rewritten to fix 4179818 + if (maximumDigits >= 0 && maximumDigits < count) { + if (shouldRoundUp(maximumDigits)) { + // Rounding up involves incrementing digits from LSD to MSD. + // In most cases this is simple, but in a worst case situation + // (9999..99) we have to adjust the decimalAt value. + for (;;) + { + --maximumDigits; + if (maximumDigits < 0) + { + // We have all 9's, so we increment to a single digit + // of one and adjust the exponent. + digits[0] = (byte) '1'; + ++decimalAt; + maximumDigits = 0; // Adjust the count + break; + } + + ++digits[maximumDigits]; + if (digits[maximumDigits] <= '9') break; + // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this + } + ++maximumDigits; // Increment for use as count + } + count = maximumDigits; + /*Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1" + Eliminate trailing zeros. [Richard/GCL] + */ + while (count > 1 && digits[count-1] == '0') { + --count; + } //[Richard/GCL] + } + } + + /** + * Utility routine to set the value of the digit list from a long + */ + public final void set(long source) + { + set(source, 0); + } + + /** + * Set the digit list to a representation of the given long value. + * @param source Value to be converted; must be >= 0 or == + * Long.MIN_VALUE. + * @param maximumDigits The most digits which should be converted. + * If maximumDigits is lower than the number of significant digits + * in source, the representation will be rounded. Ignored if <= 0. + */ + public final void set(long source, int maximumDigits) + { + // This method does not expect a negative number. However, + // "source" can be a Long.MIN_VALUE (-9223372036854775808), + // if the number being formatted is a Long.MIN_VALUE. In that + // case, it will be formatted as -Long.MIN_VALUE, a number + // which is outside the legal range of a long, but which can + // be represented by DigitList. + // [NEW] Faster implementation + if (source <= 0) { + if (source == Long.MIN_VALUE) { + decimalAt = count = MAX_LONG_DIGITS; + System.arraycopy(LONG_MIN_REP, 0, digits, 0, count); + } else { + count = 0; + decimalAt = 0; + } + } else { + int left = MAX_LONG_DIGITS; + int right; + while (source > 0) { + digits[--left] = (byte) (((long) '0') + (source % 10)); + source /= 10; + } + decimalAt = MAX_LONG_DIGITS-left; + // Don't copy trailing zeros + // we are guaranteed that there is at least one non-zero digit, + // so we don't have to check lower bounds + for (right = MAX_LONG_DIGITS - 1; digits[right] == (byte) '0'; --right) {} + count = right - left + 1; + System.arraycopy(digits, left, digits, 0, count); + } + if (maximumDigits > 0) round(maximumDigits); + } + + /** + * Set the digit list to a representation of the given BigInteger value. + * [bnf] + * @param source Value to be converted + * @param maximumDigits The most digits which should be converted. + * If maximumDigits is lower than the number of significant digits + * in source, the representation will be rounded. Ignored if <= 0. + */ + public final void set(BigInteger source, int maximumDigits) { + String stringDigits = source.toString(); + + count = decimalAt = stringDigits.length(); + + // Don't copy trailing zeros + while (count > 1 && stringDigits.charAt(count - 1) == '0') --count; + + int offset = 0; + if (stringDigits.charAt(0) == '-') { + ++offset; + --count; + --decimalAt; + } + + ensureCapacity(count, 0); + for (int i = 0; i < count; ++i) { + digits[i] = (byte) stringDigits.charAt(i + offset); + } + + if (maximumDigits > 0) round(maximumDigits); + } + + /** + * Internal method that sets this digit list to represent the + * given value. The value is given as a String of the format + * returned by BigDecimal. + * @param stringDigits value to be represented with the following + * syntax, expressed as a regular expression: -?\d*.?\d* + * Must not be an empty string. + * @param maximumDigits The most digits which should be converted. + * If maximumDigits is lower than the number of significant digits + * in source, the representation will be rounded. Ignored if <= 0. + * @param fixedPoint If true, then maximumDigits is the maximum + * fractional digits to be converted. If false, total digits. + */ + private void setBigDecimalDigits(String stringDigits, + int maximumDigits, boolean fixedPoint) { +//| // Find the first non-zero digit, the decimal, and the last non-zero digit. +//| int first=-1, last=stringDigits.length()-1, decimal=-1; +//| for (int i=0; (first<0 || decimal<0) && i<=last; ++i) { +//| char c = stringDigits.charAt(i); +//| if (c == '.') { +//| decimal = i; +//| } else if (first < 0 && (c >= '1' && c <= '9')) { +//| first = i; +//| } +//| } +//| +//| if (first < 0) { +//| clear(); +//| return; +//| } +//| +//| // At this point we know there is at least one non-zero digit, so the +//| // following loop is safe. +//| for (;;) { +//| char c = stringDigits.charAt(last); +//| if (c != '0' && c != '.') { +//| break; +//| } +//| --last; +//| } +//| +//| if (decimal < 0) { +//| decimal = stringDigits.length(); +//| } +//| +//| count = last - first; +//| if (decimal < first || decimal > last) { +//| ++count; +//| } +//| decimalAt = decimal - first; +//| if (decimalAt < 0) { +//| ++decimalAt; +//| } +//| +//| ensureCapacity(count, 0); +//| for (int i = 0; i < count; ++i) { +//| digits[i] = (byte) stringDigits.charAt(first++); +//| if (first == decimal) { +//| ++first; +//| } +//| } + + // The maxDigits here could also be Integer.MAX_VALUE + set(stringDigits, stringDigits.length()); + + // Eliminate digits beyond maximum digits to be displayed. + // Round up if appropriate. + // {dlf} Some callers depend on passing '0' to round to mean 'don't round', but + // rather than pass that information explicitly, we rely on some magic with maximumDigits + // and decimalAt. Unfortunately, this is no good, because there are cases where maximumDigits + // is zero and we do want to round, e.g. BigDecimal values -1 < x < 1. So since round + // changed to perform rounding when the argument is 0, we now force the argument + // to -1 in the situations where it matters. + round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); + } + +//#if defined(FOUNDATION10) +//#else + /** + * Set the digit list to a representation of the given BigDecimal value. + * [bnf] + * @param source Value to be converted + * @param maximumDigits The most digits which should be converted. + * If maximumDigits is lower than the number of significant digits + * in source, the representation will be rounded. Ignored if <= 0. + * @param fixedPoint If true, then maximumDigits is the maximum + * fractional digits to be converted. If false, total digits. + */ + public final void set(java.math.BigDecimal source, + int maximumDigits, boolean fixedPoint) { + setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); + } +//#endif + + /* + * Set the digit list to a representation of the given BigDecimal value. + * [bnf] + * @param source Value to be converted + * @param maximumDigits The most digits which should be converted. + * If maximumDigits is lower than the number of significant digits + * in source, the representation will be rounded. Ignored if <= 0. + * @param fixedPoint If true, then maximumDigits is the maximum + * fractional digits to be converted. If false, total digits. + */ + public final void set(com.ibm.icu.math.BigDecimal source, + int maximumDigits, boolean fixedPoint) { + setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); + } + + /** + * Returns true if this DigitList represents Long.MIN_VALUE; + * false, otherwise. This is required so that getLong() works. + */ + private boolean isLongMIN_VALUE() + { + if (decimalAt != count || count != MAX_LONG_DIGITS) + return false; + + for (int i = 0; i < count; ++i) + { + if (digits[i] != LONG_MIN_REP[i]) return false; + } + + return true; + } + + private static byte[] LONG_MIN_REP; + + static + { + // Store the representation of LONG_MIN without the leading '-' + String s = Long.toString(Long.MIN_VALUE); + LONG_MIN_REP = new byte[MAX_LONG_DIGITS]; + for (int i=0; i < MAX_LONG_DIGITS; ++i) + { + LONG_MIN_REP[i] = (byte)s.charAt(i + 1); + } + } + +// Unused -- Alan 2003-05 +// /** +// * Return the floor of the log base 10 of a given double. +// * This method compensates for inaccuracies which arise naturally when +// * computing logs, and always give the correct value. The parameter +// * must be positive and finite. +// */ +// private static final int log10(double d) +// { +// // The reason this routine is needed is that simply taking the +// // log and dividing by log10 yields a result which may be off +// // by 1 due to rounding errors. For example, the naive log10 +// // of 1.0e300 taken this way is 299, rather than 300. +// double log10 = Math.log(d) / LOG10; +// int ilog10 = (int)Math.floor(log10); +// // Positive logs could be too small, e.g. 0.99 instead of 1.0 +// if (log10 > 0 && d >= Math.pow(10, ilog10 + 1)) +// { +// ++ilog10; +// } +// // Negative logs could be too big, e.g. -0.99 instead of -1.0 +// else if (log10 < 0 && d < Math.pow(10, ilog10)) +// { +// --ilog10; +// } +// return ilog10; +// } +// +// private static final double LOG10 = Math.log(10.0); + + // (The following boilerplate methods are currently not called, + // and cannot be called by tests since this class is + // package-private. The methods may be useful in the future, so + // we do not delete them. 2003-06-11 ICU 2.6 Alan) + ///CLOVER:OFF + /** + * equality test between two digit lists. + */ + public boolean equals(Object obj) { + if (this == obj) // quick check + return true; + if (!(obj instanceof DigitList)) // (1) same object? + return false; + DigitList other = (DigitList) obj; + if (count != other.count || + decimalAt != other.decimalAt) + return false; + for (int i = 0; i < count; i++) + if (digits[i] != other.digits[i]) + return false; + return true; + } + + /** + * Generates the hash code for the digit list. + */ + public int hashCode() { + int hashcode = decimalAt; + + for (int i = 0; i < count; i++) + hashcode = hashcode * 37 + digits[i]; + + return hashcode; + } + + public String toString() + { + if (isZero()) return "0"; + StringBuffer buf = new StringBuffer("0."); + for (int i=0; i