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 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 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 */ getMantissa()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 */ getExponent()131 public char[] getExponent(){ 132 return exponent; 133 } 134 135 /** 136 * Returns new decExp in case of overflow. 137 */ applyPrecision(int decExp, char[] digits, int nDigits, int prec)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 */ fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative)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 create(boolean isNegative, int size)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 */ fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative)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 */ fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative)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