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 sun.misc;
27 
28 import java.util.Arrays;
29 
30 public class FormattedFloatingDecimal{
31 
32     public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
33 
34 
valueOf(double d, int precision, Form form)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 
getBuffer()53     private static char[] getBuffer(){
54         return (char[]) threadLocalCharBuffer.get();
55     }
56 
FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter)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
getExponentRounded()105     public int getExponentRounded() {
106         return decExponentRounded - 1;
107     }
108 
getMantissa()109     public char[] getMantissa(){
110         return mantissa;
111     }
112 
getExponent()113     public char[] getExponent(){
114         return exponent;
115     }
116 
117     /**
118      * Returns new decExp in case of overflow.
119      */
applyPrecision(int decExp, char[] digits, int nDigits, int prec)120     private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
121         if (prec >= nDigits || prec < 0) {
122             // no rounding necessary
123             return decExp;
124         }
125         if (prec == 0) {
126             // only one digit (0 or 1) is returned because the precision
127             // excludes all significant digits
128             if (digits[0] >= '5') {
129                 digits[0] = '1';
130                 Arrays.fill(digits, 1, nDigits, '0');
131                 return decExp + 1;
132             } else {
133                 Arrays.fill(digits, 0, nDigits, '0');
134                 return decExp;
135             }
136         }
137         int q = digits[prec];
138         if (q >= '5') {
139             int i = prec;
140             q = digits[--i];
141             if ( q == '9' ) {
142                 while ( q == '9' && i > 0 ){
143                     q = digits[--i];
144                 }
145                 if ( q == '9' ){
146                     // carryout! High-order 1, rest 0s, larger exp.
147                     digits[0] = '1';
148                     Arrays.fill(digits, 1, nDigits, '0');
149                     return decExp+1;
150                 }
151             }
152             digits[i] = (char)(q + 1);
153             Arrays.fill(digits, i+1, nDigits, '0');
154         } else {
155             Arrays.fill(digits, prec, nDigits, '0');
156         }
157         return decExp;
158     }
159 
160     /**
161      * Fills mantissa and exponent char arrays for compatible format.
162      */
fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative)163     private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
164         int startIndex = isNegative ? 1 : 0;
165         if (exp > 0 && exp < 8) {
166             // print digits.digits.
167             if (nDigits < exp) {
168                 int extraZeros = exp - nDigits;
169                 mantissa = create(isNegative, nDigits + extraZeros + 2);
170                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
171                 Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
172                 mantissa[startIndex + nDigits + extraZeros] = '.';
173                 mantissa[startIndex + nDigits + extraZeros+1] = '0';
174             } else if (exp < nDigits) {
175                 int t = Math.min(nDigits - exp, precision);
176                 mantissa = create(isNegative, exp + 1 + t);
177                 System.arraycopy(digits, 0, mantissa, startIndex, exp);
178                 mantissa[startIndex + exp ] = '.';
179                 System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
180             } else { // exp == digits.length
181                 mantissa = create(isNegative, nDigits + 2);
182                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
183                 mantissa[startIndex + nDigits ] = '.';
184                 mantissa[startIndex + nDigits +1] = '0';
185             }
186         } else if (exp <= 0 && exp > -3) {
187             int zeros = Math.max(0, Math.min(-exp, precision));
188             int t = Math.max(0, Math.min(nDigits, precision + exp));
189             // write '0' s before the significant digits
190             if (zeros > 0) {
191                 mantissa = create(isNegative, zeros + 2 + t);
192                 mantissa[startIndex] = '0';
193                 mantissa[startIndex+1] = '.';
194                 Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
195                 if (t > 0) {
196                     // copy only when significant digits are within the precision
197                     System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
198                 }
199             } else if (t > 0) {
200                 mantissa = create(isNegative, zeros + 2 + t);
201                 mantissa[startIndex] = '0';
202                 mantissa[startIndex + 1] = '.';
203                 // copy only when significant digits are within the precision
204                 System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
205             } else {
206                 this.mantissa = create(isNegative, 1);
207                 this.mantissa[startIndex] = '0';
208             }
209         } else {
210             if (nDigits > 1) {
211                 mantissa = create(isNegative, nDigits + 1);
212                 mantissa[startIndex] = digits[0];
213                 mantissa[startIndex + 1] = '.';
214                 System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
215             } else {
216                 mantissa = create(isNegative, 3);
217                 mantissa[startIndex] = digits[0];
218                 mantissa[startIndex + 1] = '.';
219                 mantissa[startIndex + 2] = '0';
220             }
221             int e, expStartIntex;
222             boolean isNegExp = (exp <= 0);
223             if (isNegExp) {
224                 e = -exp + 1;
225                 expStartIntex = 1;
226             } else {
227                 e = exp - 1;
228                 expStartIntex = 0;
229             }
230             // decExponent has 1, 2, or 3, digits
231             if (e <= 9) {
232                 exponent = create(isNegExp,1);
233                 exponent[expStartIntex] = (char) (e + '0');
234             } else if (e <= 99) {
235                 exponent = create(isNegExp,2);
236                 exponent[expStartIntex] = (char) (e / 10 + '0');
237                 exponent[expStartIntex+1] = (char) (e % 10 + '0');
238             } else {
239                 exponent = create(isNegExp,3);
240                 exponent[expStartIntex] = (char) (e / 100 + '0');
241                 e %= 100;
242                 exponent[expStartIntex+1] = (char) (e / 10 + '0');
243                 exponent[expStartIntex+2] = (char) (e % 10 + '0');
244             }
245         }
246     }
247 
create(boolean isNegative, int size)248     private static char[] create(boolean isNegative, int size) {
249         if(isNegative) {
250             char[] r = new char[size +1];
251             r[0] = '-';
252             return r;
253         } else {
254             return new char[size];
255         }
256     }
257 
258     /*
259      * Fills mantissa char arrays for DECIMAL_FLOAT format.
260      * Exponent should be equal to null.
261      */
fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative)262     private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
263         int startIndex = isNegative ? 1 : 0;
264         if (exp > 0) {
265             // print digits.digits.
266             if (nDigits < exp) {
267                 mantissa = create(isNegative,exp);
268                 System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
269                 Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
270                 // Do not append ".0" for formatted floats since the user
271                 // may request that it be omitted. It is added as necessary
272                 // by the Formatter.
273             } else {
274                 int t = Math.min(nDigits - exp, precision);
275                 mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
276                 System.arraycopy(digits, 0, mantissa, startIndex, exp);
277                 // Do not append ".0" for formatted floats since the user
278                 // may request that it be omitted. It is added as necessary
279                 // by the Formatter.
280                 if (t > 0) {
281                     mantissa[startIndex + exp] = '.';
282                     System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
283                 }
284             }
285         } else if (exp <= 0) {
286             int zeros = Math.max(0, Math.min(-exp, precision));
287             int t = Math.max(0, Math.min(nDigits, precision + exp));
288             // write '0' s before the significant digits
289             if (zeros > 0) {
290                 mantissa = create(isNegative, zeros + 2 + t);
291                 mantissa[startIndex] = '0';
292                 mantissa[startIndex+1] = '.';
293                 Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
294                 if (t > 0) {
295                     // copy only when significant digits are within the precision
296                     System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
297                 }
298             } else if (t > 0) {
299                 mantissa = create(isNegative, zeros + 2 + t);
300                 mantissa[startIndex] = '0';
301                 mantissa[startIndex + 1] = '.';
302                 // copy only when significant digits are within the precision
303                 System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
304             } else {
305                 this.mantissa = create(isNegative, 1);
306                 this.mantissa[startIndex] = '0';
307             }
308         }
309     }
310 
311     /**
312      * Fills mantissa and exponent char arrays for SCIENTIFIC format.
313      */
fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative)314     private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
315         int startIndex = isNegative ? 1 : 0;
316         int t = Math.max(0, Math.min(nDigits - 1, precision));
317         if (t > 0) {
318             mantissa = create(isNegative, t + 2);
319             mantissa[startIndex] = digits[0];
320             mantissa[startIndex + 1] = '.';
321             System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
322         } else {
323             mantissa = create(isNegative, 1);
324             mantissa[startIndex] = digits[0];
325         }
326         char expSign;
327         int e;
328         if (exp <= 0) {
329             expSign = '-';
330             e = -exp + 1;
331         } else {
332             expSign = '+' ;
333             e = exp - 1;
334         }
335         // decExponent has 1, 2, or 3, digits
336         if (e <= 9) {
337             exponent = new char[] { expSign,
338                     '0', (char) (e + '0') };
339         } else if (e <= 99) {
340             exponent = new char[] { expSign,
341                     (char) (e / 10 + '0'), (char) (e % 10 + '0') };
342         } else {
343             char hiExpChar = (char) (e / 100 + '0');
344             e %= 100;
345             exponent = new char[] { expSign,
346                     hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
347         }
348     }
349 }
350