1 /*
  2  * Copyright (c) 2003, 2013, 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 package jdk.internal.math;
 27 
 28 import java.util.Arrays;
 29 
 30 public class FormattedFloatingDecimal{
 31 
 32     public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
 33 
 34 
 35     public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
 36         FloatingDecimal.BinaryToASCIIConverter fdConverter =
 37                 FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
 38         return new FormattedFloatingDecimal(precision,form, fdConverter);
 39     }
 40 
 41     private int decExponentRounded;
 42     private char[] mantissa;
 43     private char[] exponent;
 44 
 45     private static final ThreadLocal<Object> threadLocalCharBuffer =
 46             new ThreadLocal<Object>() {
 47                 @Override
 48                 protected Object initialValue() {
 49                     return new char[20];
 50                 }
 51             };
 52 
 53     private static char[] getBuffer(){
 54         return (char[]) threadLocalCharBuffer.get();
 55     }
 56 
 57     private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
 58         if (fdConverter.isExceptional()) {
 59             this.mantissa = fdConverter.toJavaFormatString().toCharArray();
 60             this.exponent = null;
 61             return;
 62         }
 63         char[] digits = getBuffer();
 64         int nDigits = fdConverter.getDigits(digits);
 65         int decExp = fdConverter.getDecimalExponent();
 66         int exp;
 67         boolean isNegative = fdConverter.isNegative();
 68         switch (form) {
 69             case COMPATIBLE:
 70                 exp = decExp;
 71                 this.decExponentRounded = exp;
 72                 fillCompatible(precision, digits, nDigits, exp, isNegative);
 73                 break;
 74             case DECIMAL_FLOAT:
 75                 exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
 76                 fillDecimal(precision, digits, nDigits, exp, isNegative);
 77                 this.decExponentRounded = exp;
 78                 break;
 79             case SCIENTIFIC:
 80                 exp = applyPrecision(decExp, digits, nDigits, precision + 1);
 81                 fillScientific(precision, digits, nDigits, exp, isNegative);
 82                 this.decExponentRounded = exp;
 83                 break;
 84             case GENERAL:
 85                 exp = applyPrecision(decExp, digits, nDigits, precision);
 86                 // adjust precision to be the number of digits to right of decimal
 87                 // the real exponent to be output is actually exp - 1, not exp
 88                 if (exp - 1 < -4 || exp - 1 >= precision) {
 89                     // form = Form.SCIENTIFIC;
 90                     precision--;
 91                     fillScientific(precision, digits, nDigits, exp, isNegative);
 92                 } else {
 93                     // form = Form.DECIMAL_FLOAT;
 94                     precision = precision - exp;
 95                     fillDecimal(precision, digits, nDigits, exp, isNegative);
 96                 }
 97                 this.decExponentRounded = exp;
 98                 break;
 99             default:
100                 assert false;
101         }
102     }
103 
104     // returns the exponent after rounding has been done by applyPrecision
105     public int getExponentRounded() {
106         return decExponentRounded - 1;
107     }
108 
109     /**
110      * Returns the mantissa as a {@code char[]}.  Note that the returned value
111      * is a reference to the internal {@code char[]} containing the mantissa,
112      * therefore code invoking this method should not pass the return value to
113      * external code but should in that case make a copy.
114      *
115      * @return a reference to the internal {@code char[]} representing the
116      *         mantissa.
117      */
118     public char[] getMantissa(){
119         return mantissa;
120     }
121 
122     /**
123      * Returns the exponent as a {@code char[]}.  Note that the returned value
124      * is a reference to the internal {@code char[]} containing the exponent,
125      * therefore code invoking this method should not pass the return value to
126      * external code but should in that case make a copy.
127      *
128      * @return a reference to the internal {@code char[]} representing the
129      *         exponent.
130      */
131     public char[] getExponent(){
132         return exponent;
133     }
134 
135     /**
136      * Returns new decExp in case of overflow.
137      */
138     private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
139         if (prec >= nDigits || prec < 0) {
140             // no rounding necessary
141             return decExp;
142         }
143         if (prec == 0) {
144             // only one digit (0 or 1) is returned because the precision
145             // excludes all significant digits
146             if (digits[0] >= '5') {
147                 digits[0] = '1';
148                 Arrays.fill(digits, 1, nDigits, '0');
149                 return decExp + 1;
150             } else {
151                 Arrays.fill(digits, 0, nDigits, '0');
152                 return decExp;
153             }
154         }
155         int q = digits[prec];
156         if (q >= '5') {
157             int i = prec;
158             q = digits[--i];
159             if ( q == '9' ) {
160                 while ( q == '9' && i > 0 ){
161                     q = digits[--i];
162                 }
163                 if ( q == '9' ){
164                     // carryout! High-order 1, rest 0s, larger exp.
165                     digits[0] = '1';
166                     Arrays.fill(digits, 1, nDigits, '0');
167                     return decExp+1;
168                 }
169             }
170             digits[i] = (char)(q + 1);
171             Arrays.fill(digits, i+1, nDigits, '0');
172         } else {
173             Arrays.fill(digits, prec, nDigits, '0');
174         }
175         return decExp;
176     }
177 
178     /**
179      * Fills mantissa and exponent char arrays for compatible format.
180      */
181     private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
182         int startIndex = isNegative ? 1 : 0;
183         if (exp > 0 && exp < 8) {
184             // print digits.digits.
185             if (nDigits < exp) {
186                 int extraZeros = exp - nDigits;
187                 mantissa = create(isNegative, nDigits + extraZeros + 2);
188                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
189                 Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
190                 mantissa[startIndex + nDigits + extraZeros] = '.';
191                 mantissa[startIndex + nDigits + extraZeros+1] = '0';
192             } else if (exp < nDigits) {
193                 int t = Math.min(nDigits - exp, precision);
194                 mantissa = create(isNegative, exp + 1 + t);
195                 System.arraycopy(digits, 0, mantissa, startIndex, exp);
196                 mantissa[startIndex + exp ] = '.';
197                 System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
198             } else { // exp == digits.length
199                 mantissa = create(isNegative, nDigits + 2);
200                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
201                 mantissa[startIndex + nDigits ] = '.';
202                 mantissa[startIndex + nDigits +1] = '0';
203             }
204         } else if (exp <= 0 && exp > -3) {
205             int zeros = Math.max(0, Math.min(-exp, precision));
206             int t = Math.max(0, Math.min(nDigits, precision + exp));
207             // write '0' s before the significant digits
208             if (zeros > 0) {
209                 mantissa = create(isNegative, zeros + 2 + t);
210                 mantissa[startIndex] = '0';
211                 mantissa[startIndex+1] = '.';
212                 Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
213                 if (t > 0) {
214                     // copy only when significant digits are within the precision
215                     System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
216                 }
217             } else if (t > 0) {
218                 mantissa = create(isNegative, zeros + 2 + t);
219                 mantissa[startIndex] = '0';
220                 mantissa[startIndex + 1] = '.';
221                 // copy only when significant digits are within the precision
222                 System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
223             } else {
224                 this.mantissa = create(isNegative, 1);
225                 this.mantissa[startIndex] = '0';
226             }
227         } else {
228             if (nDigits > 1) {
229                 mantissa = create(isNegative, nDigits + 1);
230                 mantissa[startIndex] = digits[0];
231                 mantissa[startIndex + 1] = '.';
232                 System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
233             } else {
234                 mantissa = create(isNegative, 3);
235                 mantissa[startIndex] = digits[0];
236                 mantissa[startIndex + 1] = '.';
237                 mantissa[startIndex + 2] = '0';
238             }
239             int e, expStartIntex;
240             boolean isNegExp = (exp <= 0);
241             if (isNegExp) {
242                 e = -exp + 1;
243                 expStartIntex = 1;
244             } else {
245                 e = exp - 1;
246                 expStartIntex = 0;
247             }
248             // decExponent has 1, 2, or 3, digits
249             if (e <= 9) {
250                 exponent = create(isNegExp,1);
251                 exponent[expStartIntex] = (char) (e + '0');
252             } else if (e <= 99) {
253                 exponent = create(isNegExp,2);
254                 exponent[expStartIntex] = (char) (e / 10 + '0');
255                 exponent[expStartIntex+1] = (char) (e % 10 + '0');
256             } else {
257                 exponent = create(isNegExp,3);
258                 exponent[expStartIntex] = (char) (e / 100 + '0');
259                 e %= 100;
260                 exponent[expStartIntex+1] = (char) (e / 10 + '0');
261                 exponent[expStartIntex+2] = (char) (e % 10 + '0');
262             }
263         }
264     }
265 
266     private static char[] create(boolean isNegative, int size) {
267         if(isNegative) {
268             char[] r = new char[size +1];
269             r[0] = '-';
270             return r;
271         } else {
272             return new char[size];
273         }
274     }
275 
276     /*
277      * Fills mantissa char arrays for DECIMAL_FLOAT format.
278      * Exponent should be equal to null.
279      */
280     private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
281         int startIndex = isNegative ? 1 : 0;
282         if (exp > 0) {
283             // print digits.digits.
284             if (nDigits < exp) {
285                 mantissa = create(isNegative,exp);
286                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
287                 Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
288                 // Do not append ".0" for formatted floats since the user
289                 // may request that it be omitted. It is added as necessary
290                 // by the Formatter.
291             } else {
292                 int t = Math.min(nDigits - exp, precision);
293                 mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
294                 System.arraycopy(digits, 0, mantissa, startIndex, exp);
295                 // Do not append ".0" for formatted floats since the user
296                 // may request that it be omitted. It is added as necessary
297                 // by the Formatter.
298                 if (t > 0) {
299                     mantissa[startIndex + exp] = '.';
300                     System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
301                 }
302             }
303         } else if (exp <= 0) {
304             int zeros = Math.max(0, Math.min(-exp, precision));
305             int t = Math.max(0, Math.min(nDigits, precision + exp));
306             // write '0' s before the significant digits
307             if (zeros > 0) {
308                 mantissa = create(isNegative, zeros + 2 + t);
309                 mantissa[startIndex] = '0';
310                 mantissa[startIndex+1] = '.';
311                 Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
312                 if (t > 0) {
313                     // copy only when significant digits are within the precision
314                     System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
315                 }
316             } else if (t > 0) {
317                 mantissa = create(isNegative, zeros + 2 + t);
318                 mantissa[startIndex] = '0';
319                 mantissa[startIndex + 1] = '.';
320                 // copy only when significant digits are within the precision
321                 System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
322             } else {
323                 this.mantissa = create(isNegative, 1);
324                 this.mantissa[startIndex] = '0';
325             }
326         }
327     }
328 
329     /**
330      * Fills mantissa and exponent char arrays for SCIENTIFIC format.
331      */
332     private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
333         int startIndex = isNegative ? 1 : 0;
334         int t = Math.max(0, Math.min(nDigits - 1, precision));
335         if (t > 0) {
336             mantissa = create(isNegative, t + 2);
337             mantissa[startIndex] = digits[0];
338             mantissa[startIndex + 1] = '.';
339             System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
340         } else {
341             mantissa = create(isNegative, 1);
342             mantissa[startIndex] = digits[0];
343         }
344         char expSign;
345         int e;
346         if (exp <= 0) {
347             expSign = '-';
348             e = -exp + 1;
349         } else {
350             expSign = '+' ;
351             e = exp - 1;
352         }
353         // decExponent has 1, 2, or 3, digits
354         if (e <= 9) {
355             exponent = new char[] { expSign,
356                     '0', (char) (e + '0') };
357         } else if (e <= 99) {
358             exponent = new char[] { expSign,
359                     (char) (e / 10 + '0'), (char) (e % 10 + '0') };
360         } else {
361             char hiExpChar = (char) (e / 100 + '0');
362             e %= 100;
363             exponent = new char[] { expSign,
364                     hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
365         }
366     }
367 }