| /* |
| * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.internal.math; |
| |
| import java.util.Arrays; |
| |
| public class FormattedFloatingDecimal{ |
| |
| public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL }; |
| |
| |
| public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){ |
| FloatingDecimal.BinaryToASCIIConverter fdConverter = |
| FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE); |
| return new FormattedFloatingDecimal(precision,form, fdConverter); |
| } |
| |
| private int decExponentRounded; |
| private char[] mantissa; |
| private char[] exponent; |
| |
| private static final ThreadLocal<Object> threadLocalCharBuffer = |
| new ThreadLocal<Object>() { |
| @Override |
| protected Object initialValue() { |
| return new char[20]; |
| } |
| }; |
| |
| private static char[] getBuffer(){ |
| return (char[]) threadLocalCharBuffer.get(); |
| } |
| |
| private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) { |
| if (fdConverter.isExceptional()) { |
| this.mantissa = fdConverter.toJavaFormatString().toCharArray(); |
| this.exponent = null; |
| return; |
| } |
| char[] digits = getBuffer(); |
| int nDigits = fdConverter.getDigits(digits); |
| int decExp = fdConverter.getDecimalExponent(); |
| int exp; |
| boolean isNegative = fdConverter.isNegative(); |
| switch (form) { |
| case COMPATIBLE: |
| exp = decExp; |
| this.decExponentRounded = exp; |
| fillCompatible(precision, digits, nDigits, exp, isNegative); |
| break; |
| case DECIMAL_FLOAT: |
| exp = applyPrecision(decExp, digits, nDigits, decExp + precision); |
| fillDecimal(precision, digits, nDigits, exp, isNegative); |
| this.decExponentRounded = exp; |
| break; |
| case SCIENTIFIC: |
| exp = applyPrecision(decExp, digits, nDigits, precision + 1); |
| fillScientific(precision, digits, nDigits, exp, isNegative); |
| this.decExponentRounded = exp; |
| break; |
| case GENERAL: |
| exp = applyPrecision(decExp, digits, nDigits, precision); |
| // adjust precision to be the number of digits to right of decimal |
| // the real exponent to be output is actually exp - 1, not exp |
| if (exp - 1 < -4 || exp - 1 >= precision) { |
| // form = Form.SCIENTIFIC; |
| precision--; |
| fillScientific(precision, digits, nDigits, exp, isNegative); |
| } else { |
| // form = Form.DECIMAL_FLOAT; |
| precision = precision - exp; |
| fillDecimal(precision, digits, nDigits, exp, isNegative); |
| } |
| this.decExponentRounded = exp; |
| break; |
| default: |
| assert false; |
| } |
| } |
| |
| // returns the exponent after rounding has been done by applyPrecision |
| public int getExponentRounded() { |
| return decExponentRounded - 1; |
| } |
| |
| /** |
| * Returns the mantissa as a {@code char[]}. Note that the returned value |
| * is a reference to the internal {@code char[]} containing the mantissa, |
| * therefore code invoking this method should not pass the return value to |
| * external code but should in that case make a copy. |
| * |
| * @return a reference to the internal {@code char[]} representing the |
| * mantissa. |
| */ |
| public char[] getMantissa(){ |
| return mantissa; |
| } |
| |
| /** |
| * Returns the exponent as a {@code char[]}. Note that the returned value |
| * is a reference to the internal {@code char[]} containing the exponent, |
| * therefore code invoking this method should not pass the return value to |
| * external code but should in that case make a copy. |
| * |
| * @return a reference to the internal {@code char[]} representing the |
| * exponent. |
| */ |
| public char[] getExponent(){ |
| return exponent; |
| } |
| |
| /** |
| * Returns new decExp in case of overflow. |
| */ |
| private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) { |
| if (prec >= nDigits || prec < 0) { |
| // no rounding necessary |
| return decExp; |
| } |
| if (prec == 0) { |
| // only one digit (0 or 1) is returned because the precision |
| // excludes all significant digits |
| if (digits[0] >= '5') { |
| digits[0] = '1'; |
| Arrays.fill(digits, 1, nDigits, '0'); |
| return decExp + 1; |
| } else { |
| Arrays.fill(digits, 0, nDigits, '0'); |
| return decExp; |
| } |
| } |
| int q = digits[prec]; |
| if (q >= '5') { |
| int i = prec; |
| q = digits[--i]; |
| if ( q == '9' ) { |
| while ( q == '9' && i > 0 ){ |
| q = digits[--i]; |
| } |
| if ( q == '9' ){ |
| // carryout! High-order 1, rest 0s, larger exp. |
| digits[0] = '1'; |
| Arrays.fill(digits, 1, nDigits, '0'); |
| return decExp+1; |
| } |
| } |
| digits[i] = (char)(q + 1); |
| Arrays.fill(digits, i+1, nDigits, '0'); |
| } else { |
| Arrays.fill(digits, prec, nDigits, '0'); |
| } |
| return decExp; |
| } |
| |
| /** |
| * Fills mantissa and exponent char arrays for compatible format. |
| */ |
| private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) { |
| int startIndex = isNegative ? 1 : 0; |
| if (exp > 0 && exp < 8) { |
| // print digits.digits. |
| if (nDigits < exp) { |
| int extraZeros = exp - nDigits; |
| mantissa = create(isNegative, nDigits + extraZeros + 2); |
| System.arraycopy(digits, 0, mantissa, startIndex, nDigits); |
| Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0'); |
| mantissa[startIndex + nDigits + extraZeros] = '.'; |
| mantissa[startIndex + nDigits + extraZeros+1] = '0'; |
| } else if (exp < nDigits) { |
| int t = Math.min(nDigits - exp, precision); |
| mantissa = create(isNegative, exp + 1 + t); |
| System.arraycopy(digits, 0, mantissa, startIndex, exp); |
| mantissa[startIndex + exp ] = '.'; |
| System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t); |
| } else { // exp == digits.length |
| mantissa = create(isNegative, nDigits + 2); |
| System.arraycopy(digits, 0, mantissa, startIndex, nDigits); |
| mantissa[startIndex + nDigits ] = '.'; |
| mantissa[startIndex + nDigits +1] = '0'; |
| } |
| } else if (exp <= 0 && exp > -3) { |
| int zeros = Math.max(0, Math.min(-exp, precision)); |
| int t = Math.max(0, Math.min(nDigits, precision + exp)); |
| // write '0' s before the significant digits |
| if (zeros > 0) { |
| mantissa = create(isNegative, zeros + 2 + t); |
| mantissa[startIndex] = '0'; |
| mantissa[startIndex+1] = '.'; |
| Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0'); |
| if (t > 0) { |
| // copy only when significant digits are within the precision |
| System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t); |
| } |
| } else if (t > 0) { |
| mantissa = create(isNegative, zeros + 2 + t); |
| mantissa[startIndex] = '0'; |
| mantissa[startIndex + 1] = '.'; |
| // copy only when significant digits are within the precision |
| System.arraycopy(digits, 0, mantissa, startIndex + 2, t); |
| } else { |
| this.mantissa = create(isNegative, 1); |
| this.mantissa[startIndex] = '0'; |
| } |
| } else { |
| if (nDigits > 1) { |
| mantissa = create(isNegative, nDigits + 1); |
| mantissa[startIndex] = digits[0]; |
| mantissa[startIndex + 1] = '.'; |
| System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1); |
| } else { |
| mantissa = create(isNegative, 3); |
| mantissa[startIndex] = digits[0]; |
| mantissa[startIndex + 1] = '.'; |
| mantissa[startIndex + 2] = '0'; |
| } |
| int e, expStartIntex; |
| boolean isNegExp = (exp <= 0); |
| if (isNegExp) { |
| e = -exp + 1; |
| expStartIntex = 1; |
| } else { |
| e = exp - 1; |
| expStartIntex = 0; |
| } |
| // decExponent has 1, 2, or 3, digits |
| if (e <= 9) { |
| exponent = create(isNegExp,1); |
| exponent[expStartIntex] = (char) (e + '0'); |
| } else if (e <= 99) { |
| exponent = create(isNegExp,2); |
| exponent[expStartIntex] = (char) (e / 10 + '0'); |
| exponent[expStartIntex+1] = (char) (e % 10 + '0'); |
| } else { |
| exponent = create(isNegExp,3); |
| exponent[expStartIntex] = (char) (e / 100 + '0'); |
| e %= 100; |
| exponent[expStartIntex+1] = (char) (e / 10 + '0'); |
| exponent[expStartIntex+2] = (char) (e % 10 + '0'); |
| } |
| } |
| } |
| |
| private static char[] create(boolean isNegative, int size) { |
| if(isNegative) { |
| char[] r = new char[size +1]; |
| r[0] = '-'; |
| return r; |
| } else { |
| return new char[size]; |
| } |
| } |
| |
| /* |
| * Fills mantissa char arrays for DECIMAL_FLOAT format. |
| * Exponent should be equal to null. |
| */ |
| private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) { |
| int startIndex = isNegative ? 1 : 0; |
| if (exp > 0) { |
| // print digits.digits. |
| if (nDigits < exp) { |
| mantissa = create(isNegative,exp); |
| System.arraycopy(digits, 0, mantissa, startIndex, nDigits); |
| Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0'); |
| // Do not append ".0" for formatted floats since the user |
| // may request that it be omitted. It is added as necessary |
| // by the Formatter. |
| } else { |
| int t = Math.min(nDigits - exp, precision); |
| mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0)); |
| System.arraycopy(digits, 0, mantissa, startIndex, exp); |
| // Do not append ".0" for formatted floats since the user |
| // may request that it be omitted. It is added as necessary |
| // by the Formatter. |
| if (t > 0) { |
| mantissa[startIndex + exp] = '.'; |
| System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t); |
| } |
| } |
| } else if (exp <= 0) { |
| int zeros = Math.max(0, Math.min(-exp, precision)); |
| int t = Math.max(0, Math.min(nDigits, precision + exp)); |
| // write '0' s before the significant digits |
| if (zeros > 0) { |
| mantissa = create(isNegative, zeros + 2 + t); |
| mantissa[startIndex] = '0'; |
| mantissa[startIndex+1] = '.'; |
| Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0'); |
| if (t > 0) { |
| // copy only when significant digits are within the precision |
| System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t); |
| } |
| } else if (t > 0) { |
| mantissa = create(isNegative, zeros + 2 + t); |
| mantissa[startIndex] = '0'; |
| mantissa[startIndex + 1] = '.'; |
| // copy only when significant digits are within the precision |
| System.arraycopy(digits, 0, mantissa, startIndex + 2, t); |
| } else { |
| this.mantissa = create(isNegative, 1); |
| this.mantissa[startIndex] = '0'; |
| } |
| } |
| } |
| |
| /** |
| * Fills mantissa and exponent char arrays for SCIENTIFIC format. |
| */ |
| private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) { |
| int startIndex = isNegative ? 1 : 0; |
| int t = Math.max(0, Math.min(nDigits - 1, precision)); |
| if (t > 0) { |
| mantissa = create(isNegative, t + 2); |
| mantissa[startIndex] = digits[0]; |
| mantissa[startIndex + 1] = '.'; |
| System.arraycopy(digits, 1, mantissa, startIndex + 2, t); |
| } else { |
| mantissa = create(isNegative, 1); |
| mantissa[startIndex] = digits[0]; |
| } |
| char expSign; |
| int e; |
| if (exp <= 0) { |
| expSign = '-'; |
| e = -exp + 1; |
| } else { |
| expSign = '+' ; |
| e = exp - 1; |
| } |
| // decExponent has 1, 2, or 3, digits |
| if (e <= 9) { |
| exponent = new char[] { expSign, |
| '0', (char) (e + '0') }; |
| } else if (e <= 99) { |
| exponent = new char[] { expSign, |
| (char) (e / 10 + '0'), (char) (e % 10 + '0') }; |
| } else { |
| char hiExpChar = (char) (e / 100 + '0'); |
| e %= 100; |
| exponent = new char[] { expSign, |
| hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') }; |
| } |
| } |
| } |