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