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