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