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
26package sun.misc;
27
28import java.util.Arrays;
29
30public 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    public char[] getMantissa(){
110        return mantissa;
111    }
112
113    public char[] getExponent(){
114        return exponent;
115    }
116
117    /**
118     * Returns new decExp in case of overflow.
119     */
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     */
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
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     */
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     */
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