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 jdk.internal.math;
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    /**
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}
368