KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > text > DigitList


1 /*
2  * @(#)DigitList.java 1.30 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 /*
9  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
10  * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
11  *
12  * The original version of this source code and documentation is copyrighted
13  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
14  * materials are provided under terms of a License Agreement between Taligent
15  * and Sun. This technology is protected by multiple US and International
16  * patents. This notice and attribution to Taligent may not be removed.
17  * Taligent is a registered trademark of Taligent, Inc.
18  *
19  */

20
21 package java.text;
22
23 import java.math.BigDecimal JavaDoc;
24 import java.math.BigInteger JavaDoc;
25
26 /**
27  * Digit List. Private to DecimalFormat.
28  * Handles the transcoding
29  * between numeric values and strings of characters. Only handles
30  * non-negative numbers. The division of labor between DigitList and
31  * DecimalFormat is that DigitList handles the radix 10 representation
32  * issues; DecimalFormat handles the locale-specific issues such as
33  * positive/negative, grouping, decimal point, currency, and so on.
34  *
35  * A DigitList is really a representation of a floating point value.
36  * It may be an integer value; we assume that a double has sufficient
37  * precision to represent all digits of a long.
38  *
39  * The DigitList representation consists of a string of characters,
40  * which are the digits radix 10, from '0' to '9'. It also has a radix
41  * 10 exponent associated with it. The value represented by a DigitList
42  * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
43  * derived by placing all the digits of the list to the right of the
44  * decimal point, by 10^exponent.
45  *
46  * @see Locale
47  * @see Format
48  * @see NumberFormat
49  * @see DecimalFormat
50  * @see ChoiceFormat
51  * @see MessageFormat
52  * @version 1.30 12/19/03
53  * @author Mark Davis, Alan Liu
54  */

55 final class DigitList implements Cloneable JavaDoc {
56     /**
57      * The maximum number of significant digits in an IEEE 754 double, that
58      * is, in a Java double. This must not be increased, or garbage digits
59      * will be generated, and should not be decreased, or accuracy will be lost.
60      */

61     public static final int MAX_COUNT = 19; // == Long.toString(Long.MAX_VALUE).length()
62

63     /**
64      * These data members are intentionally public and can be set directly.
65      *
66      * The value represented is given by placing the decimal point before
67      * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
68      * the decimal point and the first nonzero digit are implied. If decimalAt
69      * is > count, then trailing zeros between the digits[count-1] and the
70      * decimal point are implied.
71      *
72      * Equivalently, the represented value is given by f * 10^decimalAt. Here
73      * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
74      * the right of the decimal.
75      *
76      * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
77      * don't allow denormalized numbers because our exponent is effectively of
78      * unlimited magnitude. The count value contains the number of significant
79      * digits present in digits[].
80      *
81      * Zero is represented by any DigitList with count == 0 or with each digits[i]
82      * for all i <= count == '0'.
83      */

84     public int decimalAt = 0;
85     public int count = 0;
86     public char[] digits = new char[MAX_COUNT];
87
88     private char[] data;
89
90     /**
91      * Return true if the represented number is zero.
92      */

93     boolean isZero() {
94         for (int i=0; i < count; ++i) {
95             if (digits[i] != '0') {
96                 return false;
97             }
98         }
99         return true;
100     }
101
102     /**
103      * Clears out the digits.
104      * Use before appending them.
105      * Typically, you set a series of digits with append, then at the point
106      * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
107      * then go on appending digits.
108      */

109     public void clear () {
110         decimalAt = 0;
111         count = 0;
112     }
113
114     /**
115      * Appends a digit to the list, extending the list when necessary.
116      */

117     public void append(char digit) {
118         if (count == digits.length) {
119             char[] data = new char[count + 100];
120             System.arraycopy(digits, 0, data, 0, count);
121             digits = data;
122         }
123         digits[count++] = digit;
124     }
125
126     /**
127      * Utility routine to get the value of the digit list
128      * If (count == 0) this throws a NumberFormatException, which
129      * mimics Long.parseLong().
130      */

131     public final double getDouble() {
132         if (count == 0) {
133             return 0.0;
134         }
135
136         StringBuffer JavaDoc temp = getStringBuffer();
137         temp.append('.');
138         temp.append(digits, 0, count);
139         temp.append('E');
140         temp.append(decimalAt);
141         return Double.parseDouble(temp.toString());
142     }
143
144     /**
145      * Utility routine to get the value of the digit list.
146      * If (count == 0) this returns 0, unlike Long.parseLong().
147      */

148     public final long getLong() {
149         // for now, simple implementation; later, do proper IEEE native stuff
150

151         if (count == 0) {
152             return 0;
153         }
154
155         // We have to check for this, because this is the one NEGATIVE value
156
// we represent. If we tried to just pass the digits off to parseLong,
157
// we'd get a parse failure.
158
if (isLongMIN_VALUE()) {
159             return Long.MIN_VALUE;
160         }
161
162         StringBuffer JavaDoc temp = getStringBuffer();
163         temp.append(digits, 0, count);
164         for (int i = count; i < decimalAt; ++i) {
165             temp.append('0');
166         }
167         return Long.parseLong(temp.toString());
168     }
169
170     public final BigDecimal JavaDoc getBigDecimal() {
171         if (count == 0) {
172             if (decimalAt == 0) {
173                 return BigDecimal.ZERO;
174             } else {
175                 return new BigDecimal JavaDoc("0E" + decimalAt);
176             }
177         }
178
179         StringBuffer JavaDoc temp = new StringBuffer JavaDoc(count + 12);
180         temp.append('.');
181         temp.append(digits, 0, count);
182         temp.append('E');
183         temp.append(decimalAt);
184         return new BigDecimal JavaDoc(temp.toString());
185     }
186
187     /**
188      * Return true if the number represented by this object can fit into
189      * a long.
190      * @param isPositive true if this number should be regarded as positive
191      * @param ignoreNegativeZero true if -0 should be regarded as identical to
192      * +0; otherwise they are considered distinct
193      * @return true if this number fits into a Java long
194      */

195     boolean fitsIntoLong(boolean isPositive, boolean ignoreNegativeZero) {
196         // Figure out if the result will fit in a long. We have to
197
// first look for nonzero digits after the decimal point;
198
// then check the size. If the digit count is 18 or less, then
199
// the value can definitely be represented as a long. If it is 19
200
// then it may be too large.
201

202         // Trim trailing zeros. This does not change the represented value.
203
while (count > 0 && digits[count - 1] == '0') {
204             --count;
205         }
206
207         if (count == 0) {
208             // Positive zero fits into a long, but negative zero can only
209
// be represented as a double. - bug 4162852
210
return isPositive || ignoreNegativeZero;
211         }
212
213         if (decimalAt < count || decimalAt > MAX_COUNT) {
214             return false;
215         }
216
217         if (decimalAt < MAX_COUNT) return true;
218
219         // At this point we have decimalAt == count, and count == MAX_COUNT.
220
// The number will overflow if it is larger than 9223372036854775807
221
// or smaller than -9223372036854775808.
222
for (int i=0; i<count; ++i) {
223             char dig = digits[i], max = LONG_MIN_REP[i];
224             if (dig > max) return false;
225             if (dig < max) return true;
226         }
227
228         // At this point the first count digits match. If decimalAt is less
229
// than count, then the remaining digits are zero, and we return true.
230
if (count < decimalAt) return true;
231
232         // Now we have a representation of Long.MIN_VALUE, without the leading
233
// negative sign. If this represents a positive value, then it does
234
// not fit; otherwise it fits.
235
return !isPositive;
236     }
237
238     /**
239      * Set the digit list to a representation of the given double value.
240      * This method supports fixed-point notation.
241      * @param source Value to be converted; must not be Inf, -Inf, Nan,
242      * or a value <= 0.
243      * @param maximumFractionDigits The most fractional digits which should
244      * be converted.
245      */

246     public final void set(double source, int maximumFractionDigits) {
247         set(source, maximumFractionDigits, true);
248     }
249
250     /**
251      * Set the digit list to a representation of the given double value.
252      * This method supports both fixed-point and exponential notation.
253      * @param source Value to be converted; must not be Inf, -Inf, Nan,
254      * or a value <= 0.
255      * @param maximumDigits The most fractional or total digits which should
256      * be converted.
257      * @param fixedPoint If true, then maximumDigits is the maximum
258      * fractional digits to be converted. If false, total digits.
259      */

260     final void set(double source, int maximumDigits, boolean fixedPoint) {
261         set(Double.toString(source), maximumDigits, fixedPoint);
262     }
263
264     /**
265      * Generate a representation of the form DDDDD, DDDDD.DDDDD, or
266      * DDDDDE+/-DDDDD.
267      */

268     final void set(String JavaDoc s, int maximumDigits, boolean fixedPoint) {
269         int len = s.length();
270         char[] source = getDataChars(len);
271         s.getChars(0, len, source, 0);
272
273         decimalAt = -1;
274         count = 0;
275         int exponent = 0;
276         // Number of zeros between decimal point and first non-zero digit after
277
// decimal point, for numbers < 1.
278
int leadingZerosAfterDecimal = 0;
279         boolean nonZeroDigitSeen = false;
280
281         for (int i = 0; i < len; ) {
282             char c = source[i++];
283             if (c == '.') {
284                 decimalAt = count;
285             } else if (c == 'e' || c == 'E') {
286                 exponent = parseInt(source, i, len);
287                 break;
288             } else {
289                 if (!nonZeroDigitSeen) {
290                     nonZeroDigitSeen = (c != '0');
291                     if (!nonZeroDigitSeen && decimalAt != -1)
292                         ++leadingZerosAfterDecimal;
293                 }
294                 if (nonZeroDigitSeen) {
295                     digits[count++] = c;
296                 }
297             }
298         }
299         if (decimalAt == -1) {
300             decimalAt = count;
301         }
302         if (nonZeroDigitSeen) {
303             decimalAt += exponent - leadingZerosAfterDecimal;
304         }
305
306         if (fixedPoint) {
307             // The negative of the exponent represents the number of leading
308
// zeros between the decimal and the first non-zero digit, for
309
// a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
310
// is more than the maximum fraction digits, then we have an underflow
311
// for the printed representation.
312
if (-decimalAt > maximumDigits) {
313                 // Handle an underflow to zero when we round something like
314
// 0.0009 to 2 fractional digits.
315
count = 0;
316                 return;
317             } else if (-decimalAt == maximumDigits) {
318                 // If we round 0.0009 to 3 fractional digits, then we have to
319
// create a new one digit in the least significant location.
320
if (shouldRoundUp(0)) {
321                     count = 1;
322                     ++decimalAt;
323                     digits[0] = '1';
324                 } else {
325                     count = 0;
326                 }
327                 return;
328             }
329             // else fall through
330
}
331
332         // Eliminate trailing zeros.
333
while (count > 1 && digits[count - 1] == '0') {
334             --count;
335         }
336
337         // Eliminate digits beyond maximum digits to be displayed.
338
// Round up if appropriate.
339
round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits);
340     }
341
342     /**
343      * Round the representation to the given number of digits.
344      * @param maximumDigits The maximum number of digits to be shown.
345      * Upon return, count will be less than or equal to maximumDigits.
346      */

347     private final void round(int maximumDigits) {
348         // Eliminate digits beyond maximum digits to be displayed.
349
// Round up if appropriate.
350
if (maximumDigits >= 0 && maximumDigits < count) {
351             if (shouldRoundUp(maximumDigits)) {
352                 // Rounding up involved incrementing digits from LSD to MSD.
353
// In most cases this is simple, but in a worst case situation
354
// (9999..99) we have to adjust the decimalAt value.
355
for (;;) {
356                     --maximumDigits;
357                     if (maximumDigits < 0) {
358                         // We have all 9's, so we increment to a single digit
359
// of one and adjust the exponent.
360
digits[0] = '1';
361                         ++decimalAt;
362                         maximumDigits = 0; // Adjust the count
363
break;
364                     }
365
366                     ++digits[maximumDigits];
367                     if (digits[maximumDigits] <= '9') break;
368                     // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
369
}
370                 ++maximumDigits; // Increment for use as count
371
}
372             count = maximumDigits;
373
374             // Eliminate trailing zeros.
375
while (count > 1 && digits[count-1] == '0') {
376                 --count;
377             }
378         }
379     }
380
381
382     /**
383      * Return true if truncating the representation to the given number
384      * of digits will result in an increment to the last digit. This
385      * method implements half-even rounding, the default rounding mode.
386      * [bnf]
387      * @param maximumDigits the number of digits to keep, from 0 to
388      * <code>count-1</code>. If 0, then all digits are rounded away, and
389      * this method returns true if a one should be generated (e.g., formatting
390      * 0.09 with "#.#").
391      * @return true if digit <code>maximumDigits-1</code> should be
392      * incremented
393      */

394     private boolean shouldRoundUp(int maximumDigits) {
395         boolean increment = false;
396         // Implement IEEE half-even rounding
397
if (maximumDigits < count) {
398             if (digits[maximumDigits] > '5') {
399                 return true;
400             } else if (digits[maximumDigits] == '5' ) {
401                 for (int i=maximumDigits+1; i<count; ++i) {
402                     if (digits[i] != '0') {
403                         return true;
404                     }
405                 }
406                 return maximumDigits > 0 && (digits[maximumDigits-1] % 2 != 0);
407             }
408         }
409         return false;
410     }
411
412     /**
413      * Utility routine to set the value of the digit list from a long
414      */

415     public final void set(long source) {
416         set(source, 0);
417     }
418
419     /**
420      * Set the digit list to a representation of the given long value.
421      * @param source Value to be converted; must be >= 0 or ==
422      * Long.MIN_VALUE.
423      * @param maximumDigits The most digits which should be converted.
424      * If maximumDigits is lower than the number of significant digits
425      * in source, the representation will be rounded. Ignored if <= 0.
426      */

427     public final void set(long source, int maximumDigits) {
428         // This method does not expect a negative number. However,
429
// "source" can be a Long.MIN_VALUE (-9223372036854775808),
430
// if the number being formatted is a Long.MIN_VALUE. In that
431
// case, it will be formatted as -Long.MIN_VALUE, a number
432
// which is outside the legal range of a long, but which can
433
// be represented by DigitList.
434
if (source <= 0) {
435             if (source == Long.MIN_VALUE) {
436                 decimalAt = count = MAX_COUNT;
437                 System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
438             } else {
439                 decimalAt = count = 0; // Values <= 0 format as zero
440
}
441         } else {
442             // Rewritten to improve performance. I used to call
443
// Long.toString(), which was about 4x slower than this code.
444
int left = MAX_COUNT;
445             int right;
446             while (source > 0) {
447                 digits[--left] = (char)('0' + (source % 10));
448                 source /= 10;
449             }
450             decimalAt = MAX_COUNT - left;
451             // Don't copy trailing zeros. We are guaranteed that there is at
452
// least one non-zero digit, so we don't have to check lower bounds.
453
for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
454                 ;
455             count = right - left + 1;
456             System.arraycopy(digits, left, digits, 0, count);
457         }
458         if (maximumDigits > 0) round(maximumDigits);
459     }
460
461     /**
462      * Set the digit list to a representation of the given BigDecimal value.
463      * This method supports both fixed-point and exponential notation.
464      * @param source Value to be converted; must not be a value <= 0.
465      * @param maximumDigits The most fractional or total digits which should
466      * be converted.
467      * @param fixedPoint If true, then maximumDigits is the maximum
468      * fractional digits to be converted. If false, total digits.
469      */

470     final void set(BigDecimal JavaDoc source, int maximumDigits, boolean fixedPoint) {
471         String JavaDoc s = source.toString();
472         extendDigits(s.length());
473
474         set(s, maximumDigits, fixedPoint);
475     }
476
477     /**
478      * Set the digit list to a representation of the given BigInteger value.
479      * @param source Value to be converted; must be >= 0.
480      * @param maximumDigits The most digits which should be converted.
481      * If maximumDigits is lower than the number of significant digits
482      * in source, the representation will be rounded. Ignored if <= 0.
483      */

484     final void set(BigInteger JavaDoc source, int maximumDigits) {
485         String JavaDoc s = source.toString();
486         int len = s.length();
487         extendDigits(len);
488         s.getChars(0, len, digits, 0);
489
490         decimalAt = len;
491         int right;
492         for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
493             ;
494         count = right + 1;
495
496         if (maximumDigits > 0) {
497             round(maximumDigits);
498         }
499     }
500
501     /**
502      * equality test between two digit lists.
503      */

504     public boolean equals(Object JavaDoc obj) {
505         if (this == obj) // quick check
506
return true;
507         if (!(obj instanceof DigitList JavaDoc)) // (1) same object?
508
return false;
509         DigitList JavaDoc other = (DigitList JavaDoc) obj;
510         if (count != other.count ||
511         decimalAt != other.decimalAt)
512             return false;
513         for (int i = 0; i < count; i++)
514             if (digits[i] != other.digits[i])
515                 return false;
516         return true;
517     }
518
519     /**
520      * Generates the hash code for the digit list.
521      */

522     public int hashCode() {
523         int hashcode = decimalAt;
524
525         for (int i = 0; i < count; i++) {
526             hashcode = hashcode * 37 + digits[i];
527         }
528
529         return hashcode;
530     }
531
532     /**
533      * Creates a copy of this object.
534      * @return a clone of this instance.
535      */

536     public Object JavaDoc clone() {
537         try {
538             DigitList JavaDoc other = (DigitList JavaDoc) super.clone();
539             char[] newDigits = new char[digits.length];
540             System.arraycopy(digits, 0, newDigits, 0, digits.length);
541             other.digits = newDigits;
542             return other;
543         } catch (CloneNotSupportedException JavaDoc e) {
544             throw new InternalError JavaDoc();
545         }
546     }
547
548     /**
549      * Returns true if this DigitList represents Long.MIN_VALUE;
550      * false, otherwise. This is required so that getLong() works.
551      */

552     private boolean isLongMIN_VALUE() {
553         if (decimalAt != count || count != MAX_COUNT) {
554             return false;
555         }
556
557         for (int i = 0; i < count; ++i) {
558             if (digits[i] != LONG_MIN_REP[i]) return false;
559         }
560
561         return true;
562     }
563
564     private static final int parseInt(char[] str, int offset, int strLen) {
565         char c;
566         boolean positive = true;
567         if ((c = str[offset]) == '-') {
568             positive = false;
569             offset++;
570         } else if (c == '+') {
571             offset++;
572         }
573
574         int value = 0;
575         while (offset < strLen) {
576             c = str[offset++];
577             if (c >= '0' && c <= '9') {
578                 value = value * 10 + (c - '0');
579             } else {
580                 break;
581             }
582         }
583         return positive ? value : -value;
584     }
585
586     // The digit part of -9223372036854775808L
587
private static final char[] LONG_MIN_REP = "9223372036854775808".toCharArray();
588
589     public String JavaDoc toString() {
590         if (isZero()) {
591             return "0";
592         }
593         StringBuffer JavaDoc buf = getStringBuffer();
594         buf.append("0.");
595         buf.append(digits, 0, count);
596         buf.append("x10^");
597         buf.append(decimalAt);
598         return buf.toString();
599     }
600
601     private StringBuffer JavaDoc tempBuffer;
602
603     private StringBuffer JavaDoc getStringBuffer() {
604         if (tempBuffer == null) {
605             tempBuffer = new StringBuffer JavaDoc(MAX_COUNT);
606         } else {
607             tempBuffer.setLength(0);
608         }
609         return tempBuffer;
610     }
611
612     private void extendDigits(int len) {
613         if (len > digits.length) {
614             digits = new char[len];
615         }
616     }
617
618     private final char[] getDataChars(int length) {
619         if (data == null || data.length < length) {
620             data = new char[length];
621         }
622         return data;
623     }
624 }
625
Popular Tags