1 /*
2 * J.A.D.E. Java(TM) Addition to Default Environment.
3 * Latest release available at http://jade.dautelle.com/
4 * This class is public domain (not copyrighted).
5 */
6 package org.codehaus.plexus.util;
7
8 /**
9 * <p>
10 * This class provides utility methods to parse <code>CharSequence</code> into primitive types and to format primitive
11 * types into <code>StringBuffer</code>.
12 * </p>
13 * <p>
14 * Methods from this utility class <b>do not create temporary objects</b> and are typically faster than standard library
15 * methods (e.g {@link #parseDouble} is up to 15x faster than <code>Double.parseDouble</code>).
16 * </p>
17 * For class instances, formatting is typically performed using specialized <code>java.text.Format</code>
18 * (<code>Locale</code> sensitive) and/or using conventional methods (class sensitive). For example:
19 *
20 * <pre>
21 * public class Foo {
22 * public static Foo valueOf(CharSequence chars) {...} // Parses.
23 * public StringBuffer appendTo(StringBuffer sb) {...} // Formats.
24 * public String toString() {
25 * return appendTo(new StringBuffer()).toString();
26 * }
27 * }
28 * </pre>
29 * <p>
30 * <i> This class is <b>public domain</b> (not copyrighted).</i>
31 * </p>
32 *
33 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
34 * @version 4.6, June 22, 2003
35 */
36 public final class TypeFormat {
37
38 /**
39 * Holds the characters used to represent numbers.
40 */
41 private static final char[] DIGITS = {
42 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
43 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
44 };
45
46 /**
47 * Default constructor (forbids derivation).
48 */
49 private TypeFormat() {}
50
51 /**
52 * Searches for a particular sequence within a character sequence (general purpose parsing function).
53 *
54 * @param pattern the character sequence to search for.
55 * @param chars the character sequence being searched.
56 * @param fromIndex the index in <code>chars</code> to start the search from.
57 * @return the index in the range <code>[fromIndex, chars.length()-pattern.length()]</code> or <code>-1</code> if
58 * the character sequence is not found.
59 */
60 public static int indexOf(CharSequence pattern, CharSequence chars, int fromIndex) {
61 int patternLength = pattern.length();
62 fromIndex = Math.max(0, fromIndex);
63 if (patternLength != 0) { // At least one character to search for.
64 char firstChar = pattern.charAt(0);
65 int last = chars.length() - patternLength;
66 for (int i = fromIndex; i <= last; i++) {
67 if (chars.charAt(i) == firstChar) {
68 boolean match = true;
69 for (int j = 1; j < patternLength; j++) {
70 if (chars.charAt(i + j) != pattern.charAt(j)) {
71 match = false;
72 break;
73 }
74 }
75 if (match) {
76 return i;
77 }
78 }
79 }
80 return -1;
81 } else {
82 return Math.min(0, fromIndex);
83 }
84 }
85
86 /**
87 * Parses the specified <code>CharSequence</code> as a <code>boolean</code>.
88 *
89 * @param chars the character sequence to parse.
90 * @return the corresponding <code>boolean</code>.
91 */
92 public static boolean parseBoolean(CharSequence chars) {
93 return (chars.length() == 4)
94 && (chars.charAt(0) == 't' || chars.charAt(0) == 'T')
95 && (chars.charAt(1) == 'r' || chars.charAt(1) == 'R')
96 && (chars.charAt(2) == 'u' || chars.charAt(2) == 'U')
97 && (chars.charAt(3) == 'e' || chars.charAt(3) == 'E');
98 }
99
100 /**
101 * Parses the specified <code>CharSequence</code> as a signed decimal <code>short</code>.
102 *
103 * @param chars the character sequence to parse.
104 * @return <code>parseShort(chars, 10)</code>
105 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>short</code>.
106 * @see #parseShort(CharSequence, int)
107 */
108 public static short parseShort(CharSequence chars) {
109 return parseShort(chars, 10);
110 }
111
112 /**
113 * Parses the specified <code>CharSequence</code> as a signed <code>short</code> in the specified radix. The
114 * characters in the string must all be digits of the specified radix, except the first character which may be a
115 * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
116 *
117 * @param chars the character sequence to parse.
118 * @param radix the radix to be used while parsing.
119 * @return the corresponding <code>short</code>.
120 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>short</code>.
121 */
122 public static short parseShort(CharSequence chars, int radix) {
123 try {
124 boolean isNegative = (chars.charAt(0) == '-') ? true : false;
125 int result = 0;
126 int limit = (isNegative) ? Short.MIN_VALUE : -Short.MAX_VALUE;
127 int multmin = limit / radix;
128 int length = chars.length();
129 int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
130 while (true) {
131 int digit = Character.digit(chars.charAt(i), radix);
132 int tmp = result * radix;
133 if ((digit < 0) || (result < multmin) || (tmp < limit + digit)) { // Overflow.
134 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
135 }
136 // Accumulates negatively.
137 result = tmp - digit;
138 if (++i >= length) {
139 break;
140 }
141 }
142 return (short) (isNegative ? result : -result);
143 } catch (IndexOutOfBoundsException e) {
144 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
145 }
146 }
147
148 /**
149 * Parses the specified <code>CharSequence</code> as a signed decimal <code>int</code>.
150 *
151 * @param chars the character sequence to parse.
152 * @return <code>parseInt(chars, 10)</code>
153 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>int</code>.
154 * @see #parseInt(CharSequence, int)
155 */
156 public static int parseInt(CharSequence chars) {
157 return parseInt(chars, 10);
158 }
159
160 /**
161 * Parses the specified <code>CharSequence</code> as a signed <code>int</code> in the specified radix. The
162 * characters in the string must all be digits of the specified radix, except the first character which may be a
163 * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
164 *
165 * @param chars the character sequence to parse.
166 * @param radix the radix to be used while parsing.
167 * @return the corresponding <code>int</code>.
168 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>int</code>.
169 */
170 public static int parseInt(CharSequence chars, int radix) {
171 try {
172 boolean isNegative = (chars.charAt(0) == '-') ? true : false;
173 int result = 0;
174 int limit = (isNegative) ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
175 int multmin = limit / radix;
176 int length = chars.length();
177 int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
178 while (true) {
179 int digit = Character.digit(chars.charAt(i), radix);
180 int tmp = result * radix;
181 if ((digit < 0) || (result < multmin) || (tmp < limit + digit)) { // Overflow.
182 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
183 }
184 // Accumulates negatively to avoid surprises near MAX_VALUE
185 result = tmp - digit;
186 if (++i >= length) {
187 break;
188 }
189 }
190 return isNegative ? result : -result;
191 } catch (IndexOutOfBoundsException e) {
192 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
193 }
194 }
195
196 /**
197 * Parses the specified <code>CharSequence</code> as a signed decimal <code>long</code>.
198 *
199 * @param chars the character sequence to parse.
200 * @return <code>parseLong(chars, 10)</code>
201 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>long</code>.
202 * @see #parseLong(CharSequence, int)
203 */
204 public static long parseLong(CharSequence chars) {
205 return parseLong(chars, 10);
206 }
207
208 /**
209 * Parses the specified <code>CharSequence</code> as a signed <code>long</code> in the specified radix. The
210 * characters in the string must all be digits of the specified radix, except the first character which may be a
211 * plus sign <code>'+'</code> or a minus sign <code>'-'</code>.
212 *
213 * @param chars the character sequence to parse.
214 * @param radix the radix to be used while parsing.
215 * @return the corresponding <code>long</code>.
216 * @throws NumberFormatException if the specified character sequence does not contain a parsable <code>long</code>.
217 */
218 public static long parseLong(CharSequence chars, int radix) {
219 try {
220 boolean isNegative = (chars.charAt(0) == '-') ? true : false;
221 long result = 0;
222 long limit = (isNegative) ? Long.MIN_VALUE : -Long.MAX_VALUE;
223 long multmin = limit / radix;
224 int length = chars.length();
225 int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
226 while (true) {
227 int digit = Character.digit(chars.charAt(i), radix);
228 long tmp = result * radix;
229 if ((digit < 0) || (result < multmin) || (tmp < limit + digit)) { // Overflow.
230 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
231 }
232 // Accumulates negatively to avoid surprises near MAX_VALUE
233 result = tmp - digit;
234 if (++i >= length) {
235 break;
236 }
237 }
238 return isNegative ? result : -result;
239 } catch (IndexOutOfBoundsException e) {
240 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
241 }
242 }
243
244 /**
245 * Parses this <code>CharSequence</code> as a <code>float</code>.
246 *
247 * @param chars the character sequence to parse.
248 * @return the float number represented by the specified character sequence.
249 * @throws NumberFormatException if the character sequence does not contain a parsable <code>float</code>.
250 */
251 public static float parseFloat(CharSequence chars) {
252 double d = parseDouble(chars);
253 if ((d >= Float.MIN_VALUE) && (d <= Float.MAX_VALUE)) {
254 return (float) d;
255 } else {
256 throw new NumberFormatException("Float overflow for input characters: \"" + chars.toString() + "\"");
257 }
258 }
259
260 /**
261 * Parses this <code>CharSequence</code> as a <code>double</code>.
262 *
263 * @param chars the character sequence to parse.
264 * @return the double number represented by this character sequence.
265 * @throws NumberFormatException if the character sequence does not contain a parsable <code>double</code>.
266 */
267 public static double parseDouble(CharSequence chars) throws NumberFormatException {
268 try {
269 int length = chars.length();
270 double result = 0.0;
271 int exp = 0;
272
273 boolean isNegative = (chars.charAt(0) == '-') ? true : false;
274 int i = (isNegative || (chars.charAt(0) == '+')) ? 1 : 0;
275
276 // Checks special cases NaN or Infinity.
277 if ((chars.charAt(i) == 'N') || (chars.charAt(i) == 'I')) {
278 if (chars.toString().equals("NaN")) {
279 return Double.NaN;
280 } else if (chars.subSequence(i, length).toString().equals("Infinity")) {
281 return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
282 }
283 }
284
285 // Reads decimal number.
286 boolean fraction = false;
287 while (true) {
288 char c = chars.charAt(i);
289 if ((c == '.') && (!fraction)) {
290 fraction = true;
291 } else if ((c == 'e') || (c == 'E')) {
292 break;
293 } else if ((c >= '0') && (c <= '9')) {
294 result = result * 10 + (c - '0');
295 if (fraction) {
296 exp--;
297 }
298 } else {
299 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
300 }
301 if (++i >= length) {
302 break;
303 }
304 }
305 result = isNegative ? -result : result;
306
307 // Reads exponent (if any).
308 if (i < length) {
309 i++;
310 boolean negE = (chars.charAt(i) == '-') ? true : false;
311 i = (negE || (chars.charAt(i) == '+')) ? i + 1 : i;
312 int valE = 0;
313 while (true) {
314 char c = chars.charAt(i);
315 if ((c >= '0') && (c <= '9')) {
316 valE = valE * 10 + (c - '0');
317 if (valE > 10000000) { // Hard-limit to avoid overflow.
318 valE = 10000000;
319 }
320 } else {
321 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
322 }
323 if (++i >= length) {
324 break;
325 }
326 }
327 exp += negE ? -valE : valE;
328 }
329
330 // Returns product decimal number with exponent.
331 return multE(result, exp);
332
333 } catch (IndexOutOfBoundsException e) {
334 throw new NumberFormatException("For input characters: \"" + chars.toString() + "\"");
335 }
336 }
337
338 /**
339 * Formats the specified <code>boolean</code> and appends the resulting text to the <code>StringBuffer</code>
340 * argument.
341 *
342 * @param b a <code>boolean</code>.
343 * @param sb the <code>StringBuffer</code> to append.
344 * @return the specified <code>StringBuffer</code> object.
345 * @see #parseBoolean
346 */
347 public static StringBuffer format(boolean b, StringBuffer sb) {
348 return b ? sb.append("true") : sb.append("false");
349 }
350
351 /**
352 * Formats the specified <code>short</code> and appends the resulting text (decimal representation) to the
353 * <code>StringBuffer</code> argument.
354 * <p>
355 * Note: This method is preferred to <code>StringBuffer.append(short)
356 * </code> as it does not create temporary <code>String</code> objects (several times faster for small
357 * numbers).
358 * </p>
359 *
360 * @param s the <code>short</code> number.
361 * @param sb the <code>StringBuffer</code> to append.
362 * @return the specified <code>StringBuffer</code> object.
363 * @see #parseShort
364 */
365 public static StringBuffer format(short s, StringBuffer sb) {
366 return format((int) s, sb); // Forwards to int formatting (fast).
367 }
368
369 /**
370 * Formats the specified <code>short</code> in the specified radix and appends the resulting text to the
371 * <code>StringBuffer</code> argument.
372 *
373 * @param s the <code>short</code> number.
374 * @param radix the radix.
375 * @param sb the <code>StringBuffer</code> to append.
376 * @return the specified <code>StringBuffer</code> object.
377 * @see #parseShort(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
378 */
379 public static StringBuffer format(short s, int radix, StringBuffer sb) {
380 return format((int) s, radix, sb); // Forwards to int formatting (fast).
381 }
382
383 /**
384 * Formats the specified <code>int</code> and appends the resulting text (decimal representation) to the
385 * <code>StringBuffer</code> argument.
386 * <p>
387 * Note: This method is preferred to <code>StringBuffer.append(int)
388 * </code> as it does not create temporary <code>String</code> objects (several times faster for small
389 * numbers).
390 * </p>
391 *
392 * @param i the <code>int</code> number.
393 * @param sb the <code>StringBuffer</code> to append.
394 * @return the specified <code>StringBuffer</code> object.
395 * @see #parseInt
396 */
397 public static StringBuffer format(int i, StringBuffer sb) {
398 if (i <= 0) {
399 if (i == Integer.MIN_VALUE) { // Negation would overflow.
400 return sb.append("-2147483648"); // 11 char max.
401 } else if (i == 0) {
402 return sb.append('0');
403 }
404 i = -i;
405 sb.append('-');
406 }
407 int j = 1;
408 for (; (j < 10) && (i >= INT_POW_10[j]); j++) {}
409 // POW_10[j] > i >= POW_10[j-1]
410 for (j--; j >= 0; j--) {
411 int pow10 = INT_POW_10[j];
412 int digit = i / pow10;
413 i -= digit * pow10;
414 sb.append(DIGITS[digit]);
415 }
416 return sb;
417 }
418
419 private static final int[] INT_POW_10 = new int[10];
420
421 static {
422 int pow = 1;
423 for (int i = 0; i < 10; i++) {
424 INT_POW_10[i] = pow;
425 pow *= 10;
426 }
427 }
428
429 /**
430 * Formats the specified <code>int</code> in the specified radix and appends the resulting text to the
431 * <code>StringBuffer</code> argument.
432 *
433 * @param i the <code>int</code> number.
434 * @param radix the radix.
435 * @param sb the <code>StringBuffer</code> to append.
436 * @return the specified <code>StringBuffer</code> object.
437 * @see #parseInt(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
438 */
439 public static StringBuffer format(int i, int radix, StringBuffer sb) {
440 if (radix == 10) {
441 return format(i, sb); // Faster version.
442 } else if (radix < 2 || radix > 36) {
443 throw new IllegalArgumentException("radix: " + radix);
444 }
445 if (i < 0) {
446 sb.append('-');
447 } else {
448 i = -i;
449 }
450 format2(i, radix, sb);
451 return sb;
452 }
453
454 private static void format2(int i, int radix, StringBuffer sb) {
455 if (i <= -radix) {
456 format2(i / radix, radix, sb);
457 sb.append(DIGITS[-(i % radix)]);
458 } else {
459 sb.append(DIGITS[-i]);
460 }
461 }
462
463 /**
464 * Formats the specified <code>long</code> and appends the resulting text (decimal representation) to the
465 * <code>StringBuffer</code> argument.
466 * <p>
467 * Note: This method is preferred to <code>StringBuffer.append(long)
468 * </code> as it does not create temporary <code>String</code> objects (several times faster for small
469 * numbers).
470 * </p>
471 *
472 * @param l the <code>long</code> number.
473 * @param sb the <code>StringBuffer</code> to append.
474 * @return the specified <code>StringBuffer</code> object.
475 * @see #parseLong
476 */
477 public static StringBuffer format(long l, StringBuffer sb) {
478 if (l <= 0) {
479 if (l == Long.MIN_VALUE) { // Negation would overflow.
480 return sb.append("-9223372036854775808"); // 20 characters max.
481 } else if (l == 0) {
482 return sb.append('0');
483 }
484 l = -l;
485 sb.append('-');
486 }
487 int j = 1;
488 for (; (j < 19) && (l >= LONG_POW_10[j]); j++) {}
489 // POW_10[j] > l >= POW_10[j-1]
490 for (j--; j >= 0; j--) {
491 long pow10 = LONG_POW_10[j];
492 int digit = (int) (l / pow10);
493 l -= digit * pow10;
494 sb.append(DIGITS[digit]);
495 }
496 return sb;
497 }
498
499 private static final long[] LONG_POW_10 = new long[19];
500
501 static {
502 long pow = 1;
503 for (int i = 0; i < 19; i++) {
504 LONG_POW_10[i] = pow;
505 pow *= 10;
506 }
507 }
508
509 /**
510 * Formats the specified <code>long</code> in the specified radix and appends the resulting text to the
511 * <code>StringBuffer</code> argument.
512 *
513 * @param l the <code>long</code> number.
514 * @param radix the radix.
515 * @param sb the <code>StringBuffer</code> to append.
516 * @return the specified <code>StringBuffer</code> object.
517 * @see #parseLong(CharSequence, int) throws IllegalArgumentException if radix is not in [2 .. 36] range.
518 */
519 public static StringBuffer format(long l, int radix, StringBuffer sb) {
520 if (radix == 10) {
521 return format(l, sb); // Faster version.
522 } else if (radix < 2 || radix > 36) {
523 throw new IllegalArgumentException("radix: " + radix);
524 }
525 if (l < 0) {
526 sb.append('-');
527 } else {
528 l = -l;
529 }
530 format2(l, radix, sb);
531 return sb;
532 }
533
534 private static void format2(long l, int radix, StringBuffer sb) {
535 if (l <= -radix) {
536 format2(l / radix, radix, sb);
537 sb.append(DIGITS[(int) -(l % radix)]);
538 } else {
539 sb.append(DIGITS[(int) -l]);
540 }
541 }
542
543 /**
544 * Formats the specified <code>float</code> and appends the resulting text to the <code>StringBuffer</code>
545 * argument.
546 *
547 * @param f the <code>float</code> number.
548 * @param sb the <code>StringBuffer</code> to append.
549 * @return <code>format(f, 0.0f, sb)</code>
550 * @see #format(float, float, StringBuffer)
551 */
552 public static StringBuffer format(float f, StringBuffer sb) {
553 return format(f, 0.0f, sb);
554 }
555
556 /**
557 * Formats the specified <code>float</code> and appends the resulting text to the <code>StringBuffer</code>
558 * argument; the number of significative digits is deduced from the specified precision. All digits at least as
559 * significant as the specified precision are represented. For example:
560 * <ul>
561 * <li><code>format(5.6f, 0.01f, sb)</code> appends <code>"5.60"</code></li>
562 * <li><code>format(5.6f, 0.1f, sb)</code> appends <code>"5.6"</code></li>
563 * <li><code>format(5.6f, 1f, sb)</code> appends <code>"6"</code></li>
564 * </ul>
565 * If the precision is <code>0.0f</code>, the precision is assumed to be the intrinsic <code>float</code> precision
566 * (64 bits IEEE 754 format); no formatting is performed, all significant digits are displayed and trailing zeros
567 * are removed.
568 *
569 * @param f the <code>float</code> number.
570 * @param precision the maximum weight of the last digit represented.
571 * @param sb the <code>StringBuffer</code> to append.
572 * @return the specified <code>StringBuffer</code> object.
573 * @throws IllegalArgumentException if the specified precision is negative or would result in too many digits (19+).
574 */
575 public static StringBuffer format(float f, float precision, StringBuffer sb) {
576 // Adjusts precision.
577 boolean precisionOnLastDigit;
578 if (precision > 0.0f) {
579 precisionOnLastDigit = true;
580 } else if (precision == 0.0f) {
581 if (f != 0.0f) {
582 precisionOnLastDigit = false;
583 precision = Math.max(Math.abs(f * FLOAT_RELATIVE_ERROR), Float.MIN_VALUE);
584 } else {
585 return sb.append("0.0"); // Exact zero.
586 }
587 } else {
588 throw new IllegalArgumentException("precision: Negative values not allowed");
589 }
590 return format(f, precision, precisionOnLastDigit, sb);
591 }
592
593 /**
594 * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
595 * argument.
596 * <p>
597 * Note : This method is preferred to <code>StringBuffer.append(double)
598 * </code> or even <code>String.valueOf(double)</code> as it does not create temporary
599 * <code>String</code> or <code>
600 * FloatingDecimal</code> objects (several times faster, e.g. 15x faster for
601 * <code>Double.MAX_VALUE</code>).
602 * </p>
603 *
604 * @param d the <code>double</code> number.
605 * @param sb the <code>StringBuffer</code> to append.
606 * @return <code>format(d, 0.0, sb)</code>
607 * @see #format(double, double, StringBuffer)
608 */
609 public static StringBuffer format(double d, StringBuffer sb) {
610 return format(d, 0.0, sb);
611 }
612
613 /**
614 * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
615 * argument; the number of significand digits is specified as integer argument.
616 *
617 * @param d the <code>double</code> number.
618 * @param digits the number of significand digits (excludes exponent).
619 * @param sb the <code>StringBuffer</code> to append.
620 * @return the specified <code>StringBuffer</code> object.
621 * @throws IllegalArgumentException if the number of digits is not in range <code>[1..19]</code>.
622 */
623 public static StringBuffer format(double d, int digits, StringBuffer sb) {
624 if ((digits >= 1) && (digits <= 19)) {
625 double precision = Math.abs(d / DOUBLE_POW_10[digits - 1]);
626 return format(d, precision, sb);
627 } else {
628 throw new java.lang.IllegalArgumentException("digits: " + digits + " is not in range [1 .. 19]");
629 }
630 }
631
632 /**
633 * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
634 * argument; the number of significative digits is deduced from the specified precision. All digits at least as
635 * significant as the specified precision are represented. For example:
636 * <ul>
637 * <li><code>format(5.6, 0.01, sb)</code> appends <code>"5.60"</code></li>
638 * <li><code>format(5.6, 0.1, sb)</code> appends <code>"5.6"</code></li>
639 * <li><code>format(5.6, 1, sb)</code> appends <code>"6"</code></li>
640 * </ul>
641 * If the precision is <code>0.0</code>, the precision is assumed to be the intrinsic <code>double</code> precision
642 * (64 bits IEEE 754 format); no formatting is performed, all significant digits are displayed and trailing zeros
643 * are removed.
644 *
645 * @param d the <code>double</code> number.
646 * @param precision the maximum weight of the last digit represented.
647 * @param sb the <code>StringBuffer</code> to append.
648 * @return the specified <code>StringBuffer</code> object.
649 * @throws IllegalArgumentException if the specified precision is negative or would result in too many digits (19+).
650 */
651 public static StringBuffer format(double d, double precision, StringBuffer sb) {
652 // Adjusts precision.
653 boolean precisionOnLastDigit = false;
654 if (precision > 0.0) {
655 precisionOnLastDigit = true;
656 } else if (precision == 0.0) {
657 if (d != 0.0) {
658 precision = Math.max(Math.abs(d * DOUBLE_RELATIVE_ERROR), Double.MIN_VALUE);
659 } else {
660 return sb.append("0.0"); // Exact zero.
661 }
662 } else if (precision < 0.0) { // Not NaN
663 throw new IllegalArgumentException("precision: Negative values not allowed");
664 }
665 return format(d, precision, precisionOnLastDigit, sb);
666 }
667
668 /**
669 * Formats the specified <code>double</code> and appends the resulting text to the <code>StringBuffer</code>
670 * argument; the number of significative digits is deduced from the specified precision.
671 *
672 * @param d the <code>double</code> number.
673 * @param precision the maximum weight of the last digit represented.
674 * @param precisionOnLastDigit indicates if the number of digits is deduced from the specified precision.
675 * @param sb the <code>StringBuffer</code> to append.
676 * @return the specified <code>StringBuffer</code> object.
677 */
678 private static StringBuffer format(double d, double precision, boolean precisionOnLastDigit, StringBuffer sb) {
679 // Special cases.
680 if (Double.isNaN(d)) {
681 return sb.append("NaN");
682 } else if (Double.isInfinite(d)) {
683 return (d >= 0) ? sb.append("Infinity") : sb.append("-Infinity");
684 }
685 if (d < 0) {
686 d = -d;
687 sb.append('-');
688 }
689
690 // Formats decimal part.
691 int rank = (int) Math.floor(Math.log(precision) / LOG_10);
692 double digitValue = multE(d, -rank);
693 if (digitValue >= Long.MAX_VALUE) {
694 throw new IllegalArgumentException("Specified precision would result in too many digits");
695 }
696 int digitStart = sb.length();
697 format(Math.round(digitValue), sb);
698 int digitLength = sb.length() - digitStart;
699 int dotPos = digitLength + rank;
700 boolean useScientificNotation = false;
701
702 // Inserts dot ('.')
703 if ((dotPos <= -LEADING_ZEROS.length) || (dotPos > digitLength)) {
704 // Scientific notation has to be used ("x.xxxEyy").
705 sb.insert(digitStart + 1, '.');
706 useScientificNotation = true;
707 } else if (dotPos > 0) {
708 // Dot within the string ("xxxx.xxxxx").
709 sb.insert(digitStart + dotPos, '.');
710 } else {
711 // Leading zeros ("0.xxxxx").
712 sb.insert(digitStart, LEADING_ZEROS[-dotPos]);
713 }
714
715 // Removes trailing zeros.
716 if (!precisionOnLastDigit) {
717 int newLength = sb.length();
718 do {
719 newLength--;
720 } while (sb.charAt(newLength) == '0');
721 sb.setLength(newLength + 1);
722 }
723
724 // Avoids trailing '.'
725 if (sb.charAt(sb.length() - 1) == '.') {
726 if (precisionOnLastDigit) {
727 sb.setLength(sb.length() - 1); // Prefers "xxx" to "xxx."
728 } else {
729 sb.append('0'); // Prefer "xxx.0" to "xxx."
730 }
731 }
732
733 // Writes exponent.
734 if (useScientificNotation) {
735 sb.append('E');
736 format(dotPos - 1, sb);
737 }
738
739 return sb;
740 }
741
742 private static final double LOG_10 = Math.log(10);
743
744 private static final float FLOAT_RELATIVE_ERROR = (float) Math.pow(2, -24);
745
746 private static final double DOUBLE_RELATIVE_ERROR = Math.pow(2, -53);
747
748 private static String[] LEADING_ZEROS = {"0.", "0.0", "0.00"};
749
750 /**
751 * Returns the product of the specified value with <code>10</code> raised at the specified power exponent.
752 *
753 * @param value the value.
754 * @param E the exponent.
755 * @return <code>value * 10^E</code>
756 */
757 private static final double multE(double value, int E) {
758 if (E >= 0) {
759 if (E <= 308) {
760 // Max: 1.7976931348623157E+308
761 return value * DOUBLE_POW_10[E];
762 } else {
763 value *= 1E21; // Exact multiplicand.
764 E = Math.min(308, E - 21);
765 return value * DOUBLE_POW_10[E];
766 }
767 } else {
768 if (E >= -308) {
769 return value / DOUBLE_POW_10[-E];
770 } else {
771 // Min: 4.9E-324
772 value /= 1E21; // Exact divisor.
773 E = Math.max(-308, E + 21);
774 return value / DOUBLE_POW_10[-E];
775 }
776 }
777 }
778
779 // Note: Approximation for exponents > 21. This may introduce round-off
780 // errors (e.g. 1E23 represented as "9.999999999999999E22").
781 private static final double[] DOUBLE_POW_10 = new double[] {
782 1E000, 1E001, 1E002, 1E003, 1E004, 1E005, 1E006, 1E007, 1E008, 1E009, 1E010, 1E011, 1E012, 1E013, 1E014, 1E015,
783 1E016, 1E017, 1E018, 1E019, 1E020, 1E021, 1E022, 1E023, 1E024, 1E025, 1E026, 1E027, 1E028, 1E029, 1E030, 1E031,
784 1E032, 1E033, 1E034, 1E035, 1E036, 1E037, 1E038, 1E039, 1E040, 1E041, 1E042, 1E043, 1E044, 1E045, 1E046, 1E047,
785 1E048, 1E049, 1E050, 1E051, 1E052, 1E053, 1E054, 1E055, 1E056, 1E057, 1E058, 1E059, 1E060, 1E061, 1E062, 1E063,
786 1E064, 1E065, 1E066, 1E067, 1E068, 1E069, 1E070, 1E071, 1E072, 1E073, 1E074, 1E075, 1E076, 1E077, 1E078, 1E079,
787 1E080, 1E081, 1E082, 1E083, 1E084, 1E085, 1E086, 1E087, 1E088, 1E089, 1E090, 1E091, 1E092, 1E093, 1E094, 1E095,
788 1E096, 1E097, 1E098, 1E099, 1E100, 1E101, 1E102, 1E103, 1E104, 1E105, 1E106, 1E107, 1E108, 1E109, 1E110, 1E111,
789 1E112, 1E113, 1E114, 1E115, 1E116, 1E117, 1E118, 1E119, 1E120, 1E121, 1E122, 1E123, 1E124, 1E125, 1E126, 1E127,
790 1E128, 1E129, 1E130, 1E131, 1E132, 1E133, 1E134, 1E135, 1E136, 1E137, 1E138, 1E139, 1E140, 1E141, 1E142, 1E143,
791 1E144, 1E145, 1E146, 1E147, 1E148, 1E149, 1E150, 1E151, 1E152, 1E153, 1E154, 1E155, 1E156, 1E157, 1E158, 1E159,
792 1E160, 1E161, 1E162, 1E163, 1E164, 1E165, 1E166, 1E167, 1E168, 1E169, 1E170, 1E171, 1E172, 1E173, 1E174, 1E175,
793 1E176, 1E177, 1E178, 1E179, 1E180, 1E181, 1E182, 1E183, 1E184, 1E185, 1E186, 1E187, 1E188, 1E189, 1E190, 1E191,
794 1E192, 1E193, 1E194, 1E195, 1E196, 1E197, 1E198, 1E199, 1E200, 1E201, 1E202, 1E203, 1E204, 1E205, 1E206, 1E207,
795 1E208, 1E209, 1E210, 1E211, 1E212, 1E213, 1E214, 1E215, 1E216, 1E217, 1E218, 1E219, 1E220, 1E221, 1E222, 1E223,
796 1E224, 1E225, 1E226, 1E227, 1E228, 1E229, 1E230, 1E231, 1E232, 1E233, 1E234, 1E235, 1E236, 1E237, 1E238, 1E239,
797 1E240, 1E241, 1E242, 1E243, 1E244, 1E245, 1E246, 1E247, 1E248, 1E249, 1E250, 1E251, 1E252, 1E253, 1E254, 1E255,
798 1E256, 1E257, 1E258, 1E259, 1E260, 1E261, 1E262, 1E263, 1E264, 1E265, 1E266, 1E267, 1E268, 1E269, 1E270, 1E271,
799 1E272, 1E273, 1E274, 1E275, 1E276, 1E277, 1E278, 1E279, 1E280, 1E281, 1E282, 1E283, 1E284, 1E285, 1E286, 1E287,
800 1E288, 1E289, 1E290, 1E291, 1E292, 1E293, 1E294, 1E295, 1E296, 1E297, 1E298, 1E299, 1E300, 1E301, 1E302, 1E303,
801 1E304, 1E305, 1E306, 1E307, 1E308
802 };
803 }