001 /*
002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 /*
027 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
028 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
029 *
030 * The original version of this source code and documentation is copyrighted
031 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
032 * materials are provided under terms of a License Agreement between Taligent
033 * and Sun. This technology is protected by multiple US and International
034 * patents. This notice and attribution to Taligent may not be removed.
035 * Taligent is a registered trademark of Taligent, Inc.
036 *
037 */
038
039 package java.text;
040
041 import java.math.BigDecimal;
042 import java.math.BigInteger;
043 import java.math.RoundingMode;
044
045 /**
046 * Digit List. Private to DecimalFormat.
047 * Handles the transcoding
048 * between numeric values and strings of characters. Only handles
049 * non-negative numbers. The division of labor between DigitList and
050 * DecimalFormat is that DigitList handles the radix 10 representation
051 * issues; DecimalFormat handles the locale-specific issues such as
052 * positive/negative, grouping, decimal point, currency, and so on.
053 *
054 * A DigitList is really a representation of a floating point value.
055 * It may be an integer value; we assume that a double has sufficient
056 * precision to represent all digits of a long.
057 *
058 * The DigitList representation consists of a string of characters,
059 * which are the digits radix 10, from '0' to '9'. It also has a radix
060 * 10 exponent associated with it. The value represented by a DigitList
061 * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
062 * derived by placing all the digits of the list to the right of the
063 * decimal point, by 10^exponent.
064 *
065 * @see Locale
066 * @see Format
067 * @see NumberFormat
068 * @see DecimalFormat
069 * @see ChoiceFormat
070 * @see MessageFormat
071 * @version 1.40 05/05/07
072 * @author Mark Davis, Alan Liu
073 */
074 final class DigitList implements Cloneable {
075 /**
076 * The maximum number of significant digits in an IEEE 754 double, that
077 * is, in a Java double. This must not be increased, or garbage digits
078 * will be generated, and should not be decreased, or accuracy will be lost.
079 */
080 public static final int MAX_COUNT = 19; // == Long.toString(Long.MAX_VALUE).length()
081
082 /**
083 * These data members are intentionally public and can be set directly.
084 *
085 * The value represented is given by placing the decimal point before
086 * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
087 * the decimal point and the first nonzero digit are implied. If decimalAt
088 * is > count, then trailing zeros between the digits[count-1] and the
089 * decimal point are implied.
090 *
091 * Equivalently, the represented value is given by f * 10^decimalAt. Here
092 * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
093 * the right of the decimal.
094 *
095 * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
096 * don't allow denormalized numbers because our exponent is effectively of
097 * unlimited magnitude. The count value contains the number of significant
098 * digits present in digits[].
099 *
100 * Zero is represented by any DigitList with count == 0 or with each digits[i]
101 * for all i <= count == '0'.
102 */
103 public int decimalAt = 0;
104 public int count = 0;
105 public char[] digits = new char[MAX_COUNT];
106
107 private char[] data;
108 private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
109 private boolean isNegative = false;
110
111 /**
112 * Return true if the represented number is zero.
113 */
114 boolean isZero() {
115 for (int i = 0; i < count; ++i) {
116 if (digits[i] != '0') {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 /**
124 * Set the rounding mode
125 */
126 void setRoundingMode(RoundingMode r) {
127 roundingMode = r;
128 }
129
130 /**
131 * Clears out the digits.
132 * Use before appending them.
133 * Typically, you set a series of digits with append, then at the point
134 * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
135 * then go on appending digits.
136 */
137 public void clear() {
138 decimalAt = 0;
139 count = 0;
140 }
141
142 /**
143 * Appends a digit to the list, extending the list when necessary.
144 */
145 public void append(char digit) {
146 if (count == digits.length) {
147 char[] data = new char[count + 100];
148 System.arraycopy(digits, 0, data, 0, count);
149 digits = data;
150 }
151 digits[count++] = digit;
152 }
153
154 /**
155 * Utility routine to get the value of the digit list
156 * If (count == 0) this throws a NumberFormatException, which
157 * mimics Long.parseLong().
158 */
159 public final double getDouble() {
160 if (count == 0) {
161 return 0.0;
162 }
163
164 StringBuffer temp = getStringBuffer();
165 temp.append('.');
166 temp.append(digits, 0, count);
167 temp.append('E');
168 temp.append(decimalAt);
169 return Double.parseDouble(temp.toString());
170 }
171
172 /**
173 * Utility routine to get the value of the digit list.
174 * If (count == 0) this returns 0, unlike Long.parseLong().
175 */
176 public final long getLong() {
177 // for now, simple implementation; later, do proper IEEE native stuff
178
179 if (count == 0) {
180 return 0;
181 }
182
183 // We have to check for this, because this is the one NEGATIVE value
184 // we represent. If we tried to just pass the digits off to parseLong,
185 // we'd get a parse failure.
186 if (isLongMIN_VALUE()) {
187 return Long.MIN_VALUE;
188 }
189
190 StringBuffer temp = getStringBuffer();
191 temp.append(digits, 0, count);
192 for (int i = count; i < decimalAt; ++i) {
193 temp.append('0');
194 }
195 return Long.parseLong(temp.toString());
196 }
197
198 public final BigDecimal getBigDecimal() {
199 if (count == 0) {
200 if (decimalAt == 0) {
201 return BigDecimal.ZERO;
202 } else {
203 return new BigDecimal("0E" + decimalAt);
204 }
205 }
206
207 if (decimalAt == count) {
208 return new BigDecimal(digits, 0, count);
209 } else {
210 return new BigDecimal(digits, 0, count)
211 .scaleByPowerOfTen(decimalAt - count);
212 }
213 }
214
215 /**
216 * Return true if the number represented by this object can fit into
217 * a long.
218 * @param isPositive true if this number should be regarded as positive
219 * @param ignoreNegativeZero true if -0 should be regarded as identical to
220 * +0; otherwise they are considered distinct
221 * @return true if this number fits into a Java long
222 */
223 boolean fitsIntoLong(boolean isPositive, boolean ignoreNegativeZero) {
224 // Figure out if the result will fit in a long. We have to
225 // first look for nonzero digits after the decimal point;
226 // then check the size. If the digit count is 18 or less, then
227 // the value can definitely be represented as a long. If it is 19
228 // then it may be too large.
229
230 // Trim trailing zeros. This does not change the represented value.
231 while (count > 0 && digits[count - 1] == '0') {
232 --count;
233 }
234
235 if (count == 0) {
236 // Positive zero fits into a long, but negative zero can only
237 // be represented as a double. - bug 4162852
238 return isPositive || ignoreNegativeZero;
239 }
240
241 if (decimalAt < count || decimalAt > MAX_COUNT) {
242 return false;
243 }
244
245 if (decimalAt < MAX_COUNT)
246 return true;
247
248 // At this point we have decimalAt == count, and count == MAX_COUNT.
249 // The number will overflow if it is larger than 9223372036854775807
250 // or smaller than -9223372036854775808.
251 for (int i = 0; i < count; ++i) {
252 char dig = digits[i], max = LONG_MIN_REP[i];
253 if (dig > max)
254 return false;
255 if (dig < max)
256 return true;
257 }
258
259 // At this point the first count digits match. If decimalAt is less
260 // than count, then the remaining digits are zero, and we return true.
261 if (count < decimalAt)
262 return true;
263
264 // Now we have a representation of Long.MIN_VALUE, without the leading
265 // negative sign. If this represents a positive value, then it does
266 // not fit; otherwise it fits.
267 return !isPositive;
268 }
269
270 /**
271 * Set the digit list to a representation of the given double value.
272 * This method supports fixed-point notation.
273 * @param isNegative Boolean value indicating whether the number is negative.
274 * @param source Value to be converted; must not be Inf, -Inf, Nan,
275 * or a value <= 0.
276 * @param maximumFractionDigits The most fractional digits which should
277 * be converted.
278 */
279 public final void set(boolean isNegative, double source,
280 int maximumFractionDigits) {
281 set(isNegative, source, maximumFractionDigits, true);
282 }
283
284 /**
285 * Set the digit list to a representation of the given double value.
286 * This method supports both fixed-point and exponential notation.
287 * @param isNegative Boolean value indicating whether the number is negative.
288 * @param source Value to be converted; must not be Inf, -Inf, Nan,
289 * or a value <= 0.
290 * @param maximumDigits The most fractional or total digits which should
291 * be converted.
292 * @param fixedPoint If true, then maximumDigits is the maximum
293 * fractional digits to be converted. If false, total digits.
294 */
295 final void set(boolean isNegative, double source,
296 int maximumDigits, boolean fixedPoint) {
297 set(isNegative, Double.toString(source), maximumDigits,
298 fixedPoint);
299 }
300
301 /**
302 * Generate a representation of the form DDDDD, DDDDD.DDDDD, or
303 * DDDDDE+/-DDDDD.
304 */
305 final void set(boolean isNegative, String s, int maximumDigits,
306 boolean fixedPoint) {
307 this .isNegative = isNegative;
308 int len = s.length();
309 char[] source = getDataChars(len);
310 s.getChars(0, len, source, 0);
311
312 decimalAt = -1;
313 count = 0;
314 int exponent = 0;
315 // Number of zeros between decimal point and first non-zero digit after
316 // decimal point, for numbers < 1.
317 int leadingZerosAfterDecimal = 0;
318 boolean nonZeroDigitSeen = false;
319
320 for (int i = 0; i < len;) {
321 char c = source[i++];
322 if (c == '.') {
323 decimalAt = count;
324 } else if (c == 'e' || c == 'E') {
325 exponent = parseInt(source, i, len);
326 break;
327 } else {
328 if (!nonZeroDigitSeen) {
329 nonZeroDigitSeen = (c != '0');
330 if (!nonZeroDigitSeen && decimalAt != -1)
331 ++leadingZerosAfterDecimal;
332 }
333 if (nonZeroDigitSeen) {
334 digits[count++] = c;
335 }
336 }
337 }
338 if (decimalAt == -1) {
339 decimalAt = count;
340 }
341 if (nonZeroDigitSeen) {
342 decimalAt += exponent - leadingZerosAfterDecimal;
343 }
344
345 if (fixedPoint) {
346 // The negative of the exponent represents the number of leading
347 // zeros between the decimal and the first non-zero digit, for
348 // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
349 // is more than the maximum fraction digits, then we have an underflow
350 // for the printed representation.
351 if (-decimalAt > maximumDigits) {
352 // Handle an underflow to zero when we round something like
353 // 0.0009 to 2 fractional digits.
354 count = 0;
355 return;
356 } else if (-decimalAt == maximumDigits) {
357 // If we round 0.0009 to 3 fractional digits, then we have to
358 // create a new one digit in the least significant location.
359 if (shouldRoundUp(0)) {
360 count = 1;
361 ++decimalAt;
362 digits[0] = '1';
363 } else {
364 count = 0;
365 }
366 return;
367 }
368 // else fall through
369 }
370
371 // Eliminate trailing zeros.
372 while (count > 1 && digits[count - 1] == '0') {
373 --count;
374 }
375
376 // Eliminate digits beyond maximum digits to be displayed.
377 // Round up if appropriate.
378 round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits);
379 }
380
381 /**
382 * Round the representation to the given number of digits.
383 * @param maximumDigits The maximum number of digits to be shown.
384 * Upon return, count will be less than or equal to maximumDigits.
385 */
386 private final void round(int maximumDigits) {
387 // Eliminate digits beyond maximum digits to be displayed.
388 // Round up if appropriate.
389 if (maximumDigits >= 0 && maximumDigits < count) {
390 if (shouldRoundUp(maximumDigits)) {
391 // Rounding up involved incrementing digits from LSD to MSD.
392 // In most cases this is simple, but in a worst case situation
393 // (9999..99) we have to adjust the decimalAt value.
394 for (;;) {
395 --maximumDigits;
396 if (maximumDigits < 0) {
397 // We have all 9's, so we increment to a single digit
398 // of one and adjust the exponent.
399 digits[0] = '1';
400 ++decimalAt;
401 maximumDigits = 0; // Adjust the count
402 break;
403 }
404
405 ++digits[maximumDigits];
406 if (digits[maximumDigits] <= '9')
407 break;
408 // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
409 }
410 ++maximumDigits; // Increment for use as count
411 }
412 count = maximumDigits;
413
414 // Eliminate trailing zeros.
415 while (count > 1 && digits[count - 1] == '0') {
416 --count;
417 }
418 }
419 }
420
421 /**
422 * Return true if truncating the representation to the given number
423 * of digits will result in an increment to the last digit. This
424 * method implements the rounding modes defined in the
425 * java.math.RoundingMode class.
426 * [bnf]
427 * @param maximumDigits the number of digits to keep, from 0 to
428 * <code>count-1</code>. If 0, then all digits are rounded away, and
429 * this method returns true if a one should be generated (e.g., formatting
430 * 0.09 with "#.#").
431 * @exception ArithmeticException if rounding is needed with rounding
432 * mode being set to RoundingMode.UNNECESSARY
433 * @return true if digit <code>maximumDigits-1</code> should be
434 * incremented
435 */
436 private boolean shouldRoundUp(int maximumDigits) {
437 if (maximumDigits < count) {
438 switch (roundingMode) {
439 case UP:
440 for (int i = maximumDigits; i < count; ++i) {
441 if (digits[i] != '0') {
442 return true;
443 }
444 }
445 break;
446 case DOWN:
447 break;
448 case CEILING:
449 for (int i = maximumDigits; i < count; ++i) {
450 if (digits[i] != '0') {
451 return !isNegative;
452 }
453 }
454 break;
455 case FLOOR:
456 for (int i = maximumDigits; i < count; ++i) {
457 if (digits[i] != '0') {
458 return isNegative;
459 }
460 }
461 break;
462 case HALF_UP:
463 if (digits[maximumDigits] >= '5') {
464 return true;
465 }
466 break;
467 case HALF_DOWN:
468 if (digits[maximumDigits] > '5') {
469 return true;
470 } else if (digits[maximumDigits] == '5') {
471 for (int i = maximumDigits + 1; i < count; ++i) {
472 if (digits[i] != '0') {
473 return true;
474 }
475 }
476 }
477 break;
478 case HALF_EVEN:
479 // Implement IEEE half-even rounding
480 if (digits[maximumDigits] > '5') {
481 return true;
482 } else if (digits[maximumDigits] == '5') {
483 for (int i = maximumDigits + 1; i < count; ++i) {
484 if (digits[i] != '0') {
485 return true;
486 }
487 }
488 return maximumDigits > 0
489 && (digits[maximumDigits - 1] % 2 != 0);
490 }
491 break;
492 case UNNECESSARY:
493 for (int i = maximumDigits; i < count; ++i) {
494 if (digits[i] != '0') {
495 throw new ArithmeticException(
496 "Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
497 }
498 }
499 break;
500 default:
501 assert false;
502 }
503 }
504 return false;
505 }
506
507 /**
508 * Utility routine to set the value of the digit list from a long
509 */
510 public final void set(boolean isNegative, long source) {
511 set(isNegative, source, 0);
512 }
513
514 /**
515 * Set the digit list to a representation of the given long value.
516 * @param isNegative Boolean value indicating whether the number is negative.
517 * @param source Value to be converted; must be >= 0 or ==
518 * Long.MIN_VALUE.
519 * @param maximumDigits The most digits which should be converted.
520 * If maximumDigits is lower than the number of significant digits
521 * in source, the representation will be rounded. Ignored if <= 0.
522 */
523 public final void set(boolean isNegative, long source,
524 int maximumDigits) {
525 this .isNegative = isNegative;
526
527 // This method does not expect a negative number. However,
528 // "source" can be a Long.MIN_VALUE (-9223372036854775808),
529 // if the number being formatted is a Long.MIN_VALUE. In that
530 // case, it will be formatted as -Long.MIN_VALUE, a number
531 // which is outside the legal range of a long, but which can
532 // be represented by DigitList.
533 if (source <= 0) {
534 if (source == Long.MIN_VALUE) {
535 decimalAt = count = MAX_COUNT;
536 System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
537 } else {
538 decimalAt = count = 0; // Values <= 0 format as zero
539 }
540 } else {
541 // Rewritten to improve performance. I used to call
542 // Long.toString(), which was about 4x slower than this code.
543 int left = MAX_COUNT;
544 int right;
545 while (source > 0) {
546 digits[--left] = (char) ('0' + (source % 10));
547 source /= 10;
548 }
549 decimalAt = MAX_COUNT - left;
550 // Don't copy trailing zeros. We are guaranteed that there is at
551 // least one non-zero digit, so we don't have to check lower bounds.
552 for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
553 ;
554 count = right - left + 1;
555 System.arraycopy(digits, left, digits, 0, count);
556 }
557 if (maximumDigits > 0)
558 round(maximumDigits);
559 }
560
561 /**
562 * Set the digit list to a representation of the given BigDecimal value.
563 * This method supports both fixed-point and exponential notation.
564 * @param isNegative Boolean value indicating whether the number is negative.
565 * @param source Value to be converted; must not be a value <= 0.
566 * @param maximumDigits The most fractional or total digits which should
567 * be converted.
568 * @param fixedPoint If true, then maximumDigits is the maximum
569 * fractional digits to be converted. If false, total digits.
570 */
571 final void set(boolean isNegative, BigDecimal source,
572 int maximumDigits, boolean fixedPoint) {
573 String s = source.toString();
574 extendDigits(s.length());
575
576 set(isNegative, s, maximumDigits, fixedPoint);
577 }
578
579 /**
580 * Set the digit list to a representation of the given BigInteger value.
581 * @param isNegative Boolean value indicating whether the number is negative.
582 * @param source Value to be converted; must be >= 0.
583 * @param maximumDigits The most digits which should be converted.
584 * If maximumDigits is lower than the number of significant digits
585 * in source, the representation will be rounded. Ignored if <= 0.
586 */
587 final void set(boolean isNegative, BigInteger source,
588 int maximumDigits) {
589 this .isNegative = isNegative;
590 String s = source.toString();
591 int len = s.length();
592 extendDigits(len);
593 s.getChars(0, len, digits, 0);
594
595 decimalAt = len;
596 int right;
597 for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
598 ;
599 count = right + 1;
600
601 if (maximumDigits > 0) {
602 round(maximumDigits);
603 }
604 }
605
606 /**
607 * equality test between two digit lists.
608 */
609 public boolean equals(Object obj) {
610 if (this == obj) // quick check
611 return true;
612 if (!(obj instanceof DigitList)) // (1) same object?
613 return false;
614 DigitList other = (DigitList) obj;
615 if (count != other.count || decimalAt != other.decimalAt)
616 return false;
617 for (int i = 0; i < count; i++)
618 if (digits[i] != other.digits[i])
619 return false;
620 return true;
621 }
622
623 /**
624 * Generates the hash code for the digit list.
625 */
626 public int hashCode() {
627 int hashcode = decimalAt;
628
629 for (int i = 0; i < count; i++) {
630 hashcode = hashcode * 37 + digits[i];
631 }
632
633 return hashcode;
634 }
635
636 /**
637 * Creates a copy of this object.
638 * @return a clone of this instance.
639 */
640 public Object clone() {
641 try {
642 DigitList other = (DigitList) super .clone();
643 char[] newDigits = new char[digits.length];
644 System.arraycopy(digits, 0, newDigits, 0, digits.length);
645 other.digits = newDigits;
646 other.tempBuffer = null;
647 return other;
648 } catch (CloneNotSupportedException e) {
649 throw new InternalError();
650 }
651 }
652
653 /**
654 * Returns true if this DigitList represents Long.MIN_VALUE;
655 * false, otherwise. This is required so that getLong() works.
656 */
657 private boolean isLongMIN_VALUE() {
658 if (decimalAt != count || count != MAX_COUNT) {
659 return false;
660 }
661
662 for (int i = 0; i < count; ++i) {
663 if (digits[i] != LONG_MIN_REP[i])
664 return false;
665 }
666
667 return true;
668 }
669
670 private static final int parseInt(char[] str, int offset, int strLen) {
671 char c;
672 boolean positive = true;
673 if ((c = str[offset]) == '-') {
674 positive = false;
675 offset++;
676 } else if (c == '+') {
677 offset++;
678 }
679
680 int value = 0;
681 while (offset < strLen) {
682 c = str[offset++];
683 if (c >= '0' && c <= '9') {
684 value = value * 10 + (c - '0');
685 } else {
686 break;
687 }
688 }
689 return positive ? value : -value;
690 }
691
692 // The digit part of -9223372036854775808L
693 private static final char[] LONG_MIN_REP = "9223372036854775808"
694 .toCharArray();
695
696 public String toString() {
697 if (isZero()) {
698 return "0";
699 }
700 StringBuffer buf = getStringBuffer();
701 buf.append("0.");
702 buf.append(digits, 0, count);
703 buf.append("x10^");
704 buf.append(decimalAt);
705 return buf.toString();
706 }
707
708 private StringBuffer tempBuffer;
709
710 private StringBuffer getStringBuffer() {
711 if (tempBuffer == null) {
712 tempBuffer = new StringBuffer(MAX_COUNT);
713 } else {
714 tempBuffer.setLength(0);
715 }
716 return tempBuffer;
717 }
718
719 private void extendDigits(int len) {
720 if (len > digits.length) {
721 digits = new char[len];
722 }
723 }
724
725 private final char[] getDataChars(int length) {
726 if (data == null || data.length < length) {
727 data = new char[length];
728 }
729 return data;
730 }
731 }
|