1. /*
  2. * @(#)DigitList.java 1.28 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  9. * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
  10. *
  11. * The original version of this source code and documentation is copyrighted
  12. * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  13. * materials are provided under terms of a License Agreement between Taligent
  14. * and Sun. This technology is protected by multiple US and International
  15. * patents. This notice and attribution to Taligent may not be removed.
  16. * Taligent is a registered trademark of Taligent, Inc.
  17. *
  18. */
  19. package java.text;
  20. /**
  21. * Digit List. Private to DecimalFormat.
  22. * Handles the transcoding
  23. * between numeric values and strings of characters. Only handles
  24. * non-negative numbers. The division of labor between DigitList and
  25. * DecimalFormat is that DigitList handles the radix 10 representation
  26. * issues; DecimalFormat handles the locale-specific issues such as
  27. * positive/negative, grouping, decimal point, currency, and so on.
  28. *
  29. * A DigitList is really a representation of a floating point value.
  30. * It may be an integer value; we assume that a double has sufficient
  31. * precision to represent all digits of a long.
  32. *
  33. * The DigitList representation consists of a string of characters,
  34. * which are the digits radix 10, from '0' to '9'. It also has a radix
  35. * 10 exponent associated with it. The value represented by a DigitList
  36. * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
  37. * derived by placing all the digits of the list to the right of the
  38. * decimal point, by 10^exponent.
  39. *
  40. * @see Locale
  41. * @see Format
  42. * @see NumberFormat
  43. * @see DecimalFormat
  44. * @see ChoiceFormat
  45. * @see MessageFormat
  46. * @version 1.28 01/23/03
  47. * @author Mark Davis, Alan Liu
  48. */
  49. final class DigitList implements Cloneable {
  50. /**
  51. * The maximum number of significant digits in an IEEE 754 double, that
  52. * is, in a Java double. This must not be increased, or garbage digits
  53. * will be generated, and should not be decreased, or accuracy will be lost.
  54. */
  55. public static final int MAX_COUNT = 19; // == Long.toString(Long.MAX_VALUE).length()
  56. public static final int DBL_DIG = 17;
  57. /**
  58. * These data members are intentionally public and can be set directly.
  59. *
  60. * The value represented is given by placing the decimal point before
  61. * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
  62. * the decimal point and the first nonzero digit are implied. If decimalAt
  63. * is > count, then trailing zeros between the digits[count-1] and the
  64. * decimal point are implied.
  65. *
  66. * Equivalently, the represented value is given by f * 10^decimalAt. Here
  67. * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
  68. * the right of the decimal.
  69. *
  70. * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
  71. * don't allow denormalized numbers because our exponent is effectively of
  72. * unlimited magnitude. The count value contains the number of significant
  73. * digits present in digits[].
  74. *
  75. * Zero is represented by any DigitList with count == 0 or with each digits[i]
  76. * for all i <= count == '0'.
  77. */
  78. public int decimalAt = 0;
  79. public int count = 0;
  80. public char[] digits = new char[MAX_COUNT];
  81. /**
  82. * Return true if the represented number is zero.
  83. */
  84. boolean isZero()
  85. {
  86. for (int i=0; i<count; ++i) if (digits[i] != '0') return false;
  87. return true;
  88. }
  89. /**
  90. * Clears out the digits.
  91. * Use before appending them.
  92. * Typically, you set a series of digits with append, then at the point
  93. * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
  94. * then go on appending digits.
  95. */
  96. public void clear () {
  97. decimalAt = 0;
  98. count = 0;
  99. }
  100. /**
  101. * Appends a digit to the list. Ignores all digits over MAX_COUNT,
  102. * since they are not significant for either longs or doubles.
  103. */
  104. public void append(char digit) {
  105. if (count < MAX_COUNT)
  106. digits[count++] = digit;
  107. }
  108. /**
  109. * Utility routine to get the value of the digit list
  110. * If (count == 0) this throws a NumberFormatException, which
  111. * mimics Long.parseLong().
  112. */
  113. public final double getDouble() {
  114. if (count == 0) return 0.0;
  115. StringBuffer temp = getStringBuffer();
  116. temp.append('.').append(digits, 0, count);
  117. temp.append('E');
  118. temp.append(decimalAt);
  119. return Double.parseDouble(temp.toString());
  120. }
  121. /**
  122. * Utility routine to get the value of the digit list.
  123. * If (count == 0) this returns 0, unlike Long.parseLong().
  124. */
  125. public final long getLong() {
  126. // for now, simple implementation; later, do proper IEEE native stuff
  127. if (count == 0) return 0;
  128. // We have to check for this, because this is the one NEGATIVE value
  129. // we represent. If we tried to just pass the digits off to parseLong,
  130. // we'd get a parse failure.
  131. if (isLongMIN_VALUE()) return Long.MIN_VALUE;
  132. StringBuffer temp = getStringBuffer();
  133. temp.append(digits, 0, count);
  134. for (int i = count; i < decimalAt; ++i) {
  135. temp.append('0');
  136. }
  137. return Long.parseLong(temp.toString());
  138. }
  139. /**
  140. * Return true if the number represented by this object can fit into
  141. * a long.
  142. * @param isPositive true if this number should be regarded as positive
  143. * @param ignoreNegativeZero true if -0 should be regarded as identical to
  144. * +0; otherwise they are considered distinct
  145. * @return true if this number fits into a Java long
  146. */
  147. boolean fitsIntoLong(boolean isPositive, boolean ignoreNegativeZero)
  148. {
  149. // Figure out if the result will fit in a long. We have to
  150. // first look for nonzero digits after the decimal point;
  151. // then check the size. If the digit count is 18 or less, then
  152. // the value can definitely be represented as a long. If it is 19
  153. // then it may be too large.
  154. // Trim trailing zeros. This does not change the represented value.
  155. while (count > 0 && digits[count - 1] == '0')
  156. --count;
  157. if (count == 0) {
  158. // Positive zero fits into a long, but negative zero can only
  159. // be represented as a double. - bug 4162852
  160. return isPositive || ignoreNegativeZero;
  161. }
  162. if (decimalAt < count || decimalAt > MAX_COUNT) return false;
  163. if (decimalAt < MAX_COUNT) return true;
  164. // At this point we have decimalAt == count, and count == MAX_COUNT.
  165. // The number will overflow if it is larger than 9223372036854775807
  166. // or smaller than -9223372036854775808.
  167. for (int i=0; i<count; ++i)
  168. {
  169. char dig = digits[i], max = LONG_MIN_REP[i];
  170. if (dig > max) return false;
  171. if (dig < max) return true;
  172. }
  173. // At this point the first count digits match. If decimalAt is less
  174. // than count, then the remaining digits are zero, and we return true.
  175. if (count < decimalAt) return true;
  176. // Now we have a representation of Long.MIN_VALUE, without the leading
  177. // negative sign. If this represents a positive value, then it does
  178. // not fit; otherwise it fits.
  179. return !isPositive;
  180. }
  181. /**
  182. * Set the digit list to a representation of the given double value.
  183. * This method supports fixed-point notation.
  184. * @param source Value to be converted; must not be Inf, -Inf, Nan,
  185. * or a value <= 0.
  186. * @param maximumFractionDigits The most fractional digits which should
  187. * be converted.
  188. */
  189. public final void set(double source, int maximumFractionDigits)
  190. {
  191. set(source, maximumFractionDigits, true);
  192. }
  193. /**
  194. * Set the digit list to a representation of the given double value.
  195. * This method supports both fixed-point and exponential notation.
  196. * @param source Value to be converted; must not be Inf, -Inf, Nan,
  197. * or a value <= 0.
  198. * @param maximumDigits The most fractional or total digits which should
  199. * be converted.
  200. * @param fixedPoint If true, then maximumDigits is the maximum
  201. * fractional digits to be converted. If false, total digits.
  202. */
  203. final void set(double source, int maximumDigits, boolean fixedPoint)
  204. {
  205. if (source == 0) source = 0;
  206. // Generate a representation of the form DDDDD, DDDDD.DDDDD, or
  207. // DDDDDE+/-DDDDD.
  208. char[] rep = Double.toString(source).toCharArray();
  209. decimalAt = -1;
  210. count = 0;
  211. int exponent = 0;
  212. // Number of zeros between decimal point and first non-zero digit after
  213. // decimal point, for numbers < 1.
  214. int leadingZerosAfterDecimal = 0;
  215. boolean nonZeroDigitSeen = false;
  216. for (int i = 0; i < rep.length; ) {
  217. char c = rep[i++];
  218. if (c == '.') {
  219. decimalAt = count;
  220. } else if (c == 'e' || c == 'E') {
  221. exponent = parseInt(rep, i);
  222. break;
  223. } else if (count < MAX_COUNT) {
  224. if (!nonZeroDigitSeen) {
  225. nonZeroDigitSeen = (c != '0');
  226. if (!nonZeroDigitSeen && decimalAt != -1)
  227. ++leadingZerosAfterDecimal;
  228. }
  229. if (nonZeroDigitSeen)
  230. digits[count++] = c;
  231. }
  232. }
  233. if (decimalAt == -1)
  234. decimalAt = count;
  235. if (nonZeroDigitSeen) {
  236. decimalAt += exponent - leadingZerosAfterDecimal;
  237. }
  238. if (fixedPoint)
  239. {
  240. // The negative of the exponent represents the number of leading
  241. // zeros between the decimal and the first non-zero digit, for
  242. // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
  243. // is more than the maximum fraction digits, then we have an underflow
  244. // for the printed representation.
  245. if (-decimalAt > maximumDigits) {
  246. // Handle an underflow to zero when we round something like
  247. // 0.0009 to 2 fractional digits.
  248. count = 0;
  249. return;
  250. } else if (-decimalAt == maximumDigits) {
  251. // If we round 0.0009 to 3 fractional digits, then we have to
  252. // create a new one digit in the least significant location.
  253. if (shouldRoundUp(0)) {
  254. count = 1;
  255. ++decimalAt;
  256. digits[0] = '1';
  257. } else {
  258. count = 0;
  259. }
  260. return;
  261. }
  262. // else fall through
  263. }
  264. // Eliminate trailing zeros.
  265. while (count > 1 && digits[count - 1] == '0')
  266. --count;
  267. // Eliminate digits beyond maximum digits to be displayed.
  268. // Round up if appropriate.
  269. round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits);
  270. }
  271. /**
  272. * Round the representation to the given number of digits.
  273. * @param maximumDigits The maximum number of digits to be shown.
  274. * Upon return, count will be less than or equal to maximumDigits.
  275. */
  276. private final void round(int maximumDigits)
  277. {
  278. // Eliminate digits beyond maximum digits to be displayed.
  279. // Round up if appropriate.
  280. if (maximumDigits >= 0 && maximumDigits < count)
  281. {
  282. if (shouldRoundUp(maximumDigits)) {
  283. // Rounding up involved incrementing digits from LSD to MSD.
  284. // In most cases this is simple, but in a worst case situation
  285. // (9999..99) we have to adjust the decimalAt value.
  286. for (;;)
  287. {
  288. --maximumDigits;
  289. if (maximumDigits < 0)
  290. {
  291. // We have all 9's, so we increment to a single digit
  292. // of one and adjust the exponent.
  293. digits[0] = '1';
  294. ++decimalAt;
  295. maximumDigits = 0; // Adjust the count
  296. break;
  297. }
  298. ++digits[maximumDigits];
  299. if (digits[maximumDigits] <= '9') break;
  300. // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
  301. }
  302. ++maximumDigits; // Increment for use as count
  303. }
  304. count = maximumDigits;
  305. // Eliminate trailing zeros.
  306. while (count > 1 && digits[count-1] == '0') {
  307. --count;
  308. }
  309. }
  310. }
  311. /**
  312. * Return true if truncating the representation to the given number
  313. * of digits will result in an increment to the last digit. This
  314. * method implements half-even rounding, the default rounding mode.
  315. * [bnf]
  316. * @param maximumDigits the number of digits to keep, from 0 to
  317. * <code>count-1</code>. If 0, then all digits are rounded away, and
  318. * this method returns true if a one should be generated (e.g., formatting
  319. * 0.09 with "#.#").
  320. * @return true if digit <code>maximumDigits-1</code> should be
  321. * incremented
  322. */
  323. private boolean shouldRoundUp(int maximumDigits) {
  324. boolean increment = false;
  325. // Implement IEEE half-even rounding
  326. if (maximumDigits < count) {
  327. if (digits[maximumDigits] > '5') {
  328. return true;
  329. } else if (digits[maximumDigits] == '5' ) {
  330. for (int i=maximumDigits+1; i<count; ++i) {
  331. if (digits[i] != '0') {
  332. return true;
  333. }
  334. }
  335. return maximumDigits > 0 && (digits[maximumDigits-1] % 2 != 0);
  336. }
  337. }
  338. return false;
  339. }
  340. /**
  341. * Utility routine to set the value of the digit list from a long
  342. */
  343. public final void set(long source)
  344. {
  345. set(source, 0);
  346. }
  347. /**
  348. * Set the digit list to a representation of the given long value.
  349. * @param source Value to be converted; must be >= 0 or ==
  350. * Long.MIN_VALUE.
  351. * @param maximumDigits The most digits which should be converted.
  352. * If maximumDigits is lower than the number of significant digits
  353. * in source, the representation will be rounded. Ignored if <= 0.
  354. */
  355. public final void set(long source, int maximumDigits)
  356. {
  357. // This method does not expect a negative number. However,
  358. // "source" can be a Long.MIN_VALUE (-9223372036854775808),
  359. // if the number being formatted is a Long.MIN_VALUE. In that
  360. // case, it will be formatted as -Long.MIN_VALUE, a number
  361. // which is outside the legal range of a long, but which can
  362. // be represented by DigitList.
  363. if (source <= 0) {
  364. if (source == Long.MIN_VALUE) {
  365. decimalAt = count = MAX_COUNT;
  366. System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
  367. } else {
  368. decimalAt = count = 0; // Values <= 0 format as zero
  369. }
  370. } else {
  371. // Rewritten to improve performance. I used to call
  372. // Long.toString(), which was about 4x slower than this code.
  373. int left = MAX_COUNT;
  374. int right;
  375. while (source > 0) {
  376. digits[--left] = (char)('0' + (source % 10));
  377. source /= 10;
  378. }
  379. decimalAt = MAX_COUNT - left;
  380. // Don't copy trailing zeros. We are guaranteed that there is at
  381. // least one non-zero digit, so we don't have to check lower bounds.
  382. for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
  383. ;
  384. count = right - left + 1;
  385. System.arraycopy(digits, left, digits, 0, count);
  386. }
  387. if (maximumDigits > 0) round(maximumDigits);
  388. }
  389. /**
  390. * equality test between two digit lists.
  391. */
  392. public boolean equals(Object obj) {
  393. if (this == obj) // quick check
  394. return true;
  395. if (!(obj instanceof DigitList)) // (1) same object?
  396. return false;
  397. DigitList other = (DigitList) obj;
  398. if (count != other.count ||
  399. decimalAt != other.decimalAt)
  400. return false;
  401. for (int i = 0; i < count; i++)
  402. if (digits[i] != other.digits[i])
  403. return false;
  404. return true;
  405. }
  406. /**
  407. * Generates the hash code for the digit list.
  408. */
  409. public int hashCode() {
  410. int hashcode = decimalAt;
  411. for (int i = 0; i < count; i++)
  412. hashcode = hashcode * 37 + digits[i];
  413. return hashcode;
  414. }
  415. /**
  416. * Creates a copy of this object.
  417. * @return a clone of this instance.
  418. */
  419. public Object clone() {
  420. try {
  421. DigitList other = (DigitList) super.clone();
  422. char[] newDigits = new char[digits.length];
  423. System.arraycopy(digits, 0, newDigits, 0, digits.length);
  424. other.digits = newDigits;
  425. return other;
  426. } catch (CloneNotSupportedException e) {
  427. throw new InternalError();
  428. }
  429. }
  430. /**
  431. * Returns true if this DigitList represents Long.MIN_VALUE;
  432. * false, otherwise. This is required so that getLong() works.
  433. */
  434. private boolean isLongMIN_VALUE()
  435. {
  436. if (decimalAt != count || count != MAX_COUNT)
  437. return false;
  438. for (int i = 0; i < count; ++i)
  439. {
  440. if (digits[i] != LONG_MIN_REP[i]) return false;
  441. }
  442. return true;
  443. }
  444. private static final int parseInt(char[] str, int offset) {
  445. char c;
  446. boolean positive = true;
  447. if ((c = str[offset]) == '-') {
  448. positive = false;
  449. offset++;
  450. } else if (c == '+') {
  451. offset++;
  452. }
  453. int value = 0;
  454. while (offset < str.length) {
  455. c = str[offset++];
  456. if (c >= '0' && c <= '9') {
  457. value = value * 10 + (c - '0');
  458. } else {
  459. break;
  460. }
  461. }
  462. return positive ? value : -value;
  463. }
  464. // The digit part of -9223372036854775808L
  465. private static final char[] LONG_MIN_REP = "9223372036854775808".toCharArray();
  466. public String toString()
  467. {
  468. if (isZero()) return "0";
  469. StringBuffer buf = getStringBuffer();
  470. buf.append("0.").append(digits, 0, count);
  471. buf.append("x10^");
  472. buf.append(decimalAt);
  473. return buf.toString();
  474. }
  475. private StringBuffer tempBuffer;
  476. private StringBuffer getStringBuffer() {
  477. if (tempBuffer == null) {
  478. tempBuffer = new StringBuffer(MAX_COUNT);
  479. } else {
  480. tempBuffer.setLength(0);
  481. }
  482. return tempBuffer;
  483. }
  484. }