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 |
|
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 } |
|