Path: blob/master/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java
41159 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.math;2627import java.util.Arrays;2829public class FormattedFloatingDecimal{3031public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };323334public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){35FloatingDecimal.BinaryToASCIIConverter fdConverter =36FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);37return new FormattedFloatingDecimal(precision,form, fdConverter);38}3940private int decExponentRounded;41private char[] mantissa;42private char[] exponent;4344private static final ThreadLocal<Object> threadLocalCharBuffer =45new ThreadLocal<Object>() {46@Override47protected Object initialValue() {48return new char[20];49}50};5152private static char[] getBuffer(){53return (char[]) threadLocalCharBuffer.get();54}5556private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {57if (fdConverter.isExceptional()) {58this.mantissa = fdConverter.toJavaFormatString().toCharArray();59this.exponent = null;60return;61}62char[] digits = getBuffer();63int nDigits = fdConverter.getDigits(digits);64int decExp = fdConverter.getDecimalExponent();65int exp;66boolean isNegative = fdConverter.isNegative();67switch (form) {68case COMPATIBLE:69exp = decExp;70this.decExponentRounded = exp;71fillCompatible(precision, digits, nDigits, exp, isNegative);72break;73case DECIMAL_FLOAT:74exp = applyPrecision(decExp, digits, nDigits, decExp + precision);75fillDecimal(precision, digits, nDigits, exp, isNegative);76this.decExponentRounded = exp;77break;78case SCIENTIFIC:79exp = applyPrecision(decExp, digits, nDigits, precision + 1);80fillScientific(precision, digits, nDigits, exp, isNegative);81this.decExponentRounded = exp;82break;83case GENERAL:84exp = applyPrecision(decExp, digits, nDigits, precision);85// adjust precision to be the number of digits to right of decimal86// the real exponent to be output is actually exp - 1, not exp87if (exp - 1 < -4 || exp - 1 >= precision) {88// form = Form.SCIENTIFIC;89precision--;90fillScientific(precision, digits, nDigits, exp, isNegative);91} else {92// form = Form.DECIMAL_FLOAT;93precision = precision - exp;94fillDecimal(precision, digits, nDigits, exp, isNegative);95}96this.decExponentRounded = exp;97break;98default:99assert false;100}101}102103// returns the exponent after rounding has been done by applyPrecision104public int getExponentRounded() {105return decExponentRounded - 1;106}107108/**109* Returns the mantissa as a {@code char[]}. Note that the returned value110* is a reference to the internal {@code char[]} containing the mantissa,111* therefore code invoking this method should not pass the return value to112* external code but should in that case make a copy.113*114* @return a reference to the internal {@code char[]} representing the115* mantissa.116*/117public char[] getMantissa(){118return mantissa;119}120121/**122* Returns the exponent as a {@code char[]}. Note that the returned value123* is a reference to the internal {@code char[]} containing the exponent,124* therefore code invoking this method should not pass the return value to125* external code but should in that case make a copy.126*127* @return a reference to the internal {@code char[]} representing the128* exponent.129*/130public char[] getExponent(){131return exponent;132}133134/**135* Returns new decExp in case of overflow.136*/137private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {138if (prec >= nDigits || prec < 0) {139// no rounding necessary140return decExp;141}142if (prec == 0) {143// only one digit (0 or 1) is returned because the precision144// excludes all significant digits145if (digits[0] >= '5') {146digits[0] = '1';147Arrays.fill(digits, 1, nDigits, '0');148return decExp + 1;149} else {150Arrays.fill(digits, 0, nDigits, '0');151return decExp;152}153}154int q = digits[prec];155if (q >= '5') {156int i = prec;157q = digits[--i];158if ( q == '9' ) {159while ( q == '9' && i > 0 ){160q = digits[--i];161}162if ( q == '9' ){163// carryout! High-order 1, rest 0s, larger exp.164digits[0] = '1';165Arrays.fill(digits, 1, nDigits, '0');166return decExp+1;167}168}169digits[i] = (char)(q + 1);170Arrays.fill(digits, i+1, nDigits, '0');171} else {172Arrays.fill(digits, prec, nDigits, '0');173}174return decExp;175}176177/**178* Fills mantissa and exponent char arrays for compatible format.179*/180private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {181int startIndex = isNegative ? 1 : 0;182if (exp > 0 && exp < 8) {183// print digits.digits.184if (nDigits < exp) {185int extraZeros = exp - nDigits;186mantissa = create(isNegative, nDigits + extraZeros + 2);187System.arraycopy(digits, 0, mantissa, startIndex, nDigits);188Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');189mantissa[startIndex + nDigits + extraZeros] = '.';190mantissa[startIndex + nDigits + extraZeros+1] = '0';191} else if (exp < nDigits) {192int t = Math.min(nDigits - exp, precision);193mantissa = create(isNegative, exp + 1 + t);194System.arraycopy(digits, 0, mantissa, startIndex, exp);195mantissa[startIndex + exp ] = '.';196System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);197} else { // exp == digits.length198mantissa = create(isNegative, nDigits + 2);199System.arraycopy(digits, 0, mantissa, startIndex, nDigits);200mantissa[startIndex + nDigits ] = '.';201mantissa[startIndex + nDigits +1] = '0';202}203} else if (exp <= 0 && exp > -3) {204int zeros = Math.max(0, Math.min(-exp, precision));205int t = Math.max(0, Math.min(nDigits, precision + exp));206// write '0' s before the significant digits207if (zeros > 0) {208mantissa = create(isNegative, zeros + 2 + t);209mantissa[startIndex] = '0';210mantissa[startIndex+1] = '.';211Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');212if (t > 0) {213// copy only when significant digits are within the precision214System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);215}216} else if (t > 0) {217mantissa = create(isNegative, zeros + 2 + t);218mantissa[startIndex] = '0';219mantissa[startIndex + 1] = '.';220// copy only when significant digits are within the precision221System.arraycopy(digits, 0, mantissa, startIndex + 2, t);222} else {223this.mantissa = create(isNegative, 1);224this.mantissa[startIndex] = '0';225}226} else {227if (nDigits > 1) {228mantissa = create(isNegative, nDigits + 1);229mantissa[startIndex] = digits[0];230mantissa[startIndex + 1] = '.';231System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);232} else {233mantissa = create(isNegative, 3);234mantissa[startIndex] = digits[0];235mantissa[startIndex + 1] = '.';236mantissa[startIndex + 2] = '0';237}238int e, expStartIntex;239boolean isNegExp = (exp <= 0);240if (isNegExp) {241e = -exp + 1;242expStartIntex = 1;243} else {244e = exp - 1;245expStartIntex = 0;246}247// decExponent has 1, 2, or 3, digits248if (e <= 9) {249exponent = create(isNegExp,1);250exponent[expStartIntex] = (char) (e + '0');251} else if (e <= 99) {252exponent = create(isNegExp,2);253exponent[expStartIntex] = (char) (e / 10 + '0');254exponent[expStartIntex+1] = (char) (e % 10 + '0');255} else {256exponent = create(isNegExp,3);257exponent[expStartIntex] = (char) (e / 100 + '0');258e %= 100;259exponent[expStartIntex+1] = (char) (e / 10 + '0');260exponent[expStartIntex+2] = (char) (e % 10 + '0');261}262}263}264265private static char[] create(boolean isNegative, int size) {266if(isNegative) {267char[] r = new char[size +1];268r[0] = '-';269return r;270} else {271return new char[size];272}273}274275/*276* Fills mantissa char arrays for DECIMAL_FLOAT format.277* Exponent should be equal to null.278*/279private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {280int startIndex = isNegative ? 1 : 0;281if (exp > 0) {282// print digits.digits.283if (nDigits < exp) {284mantissa = create(isNegative,exp);285System.arraycopy(digits, 0, mantissa, startIndex, nDigits);286Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');287// Do not append ".0" for formatted floats since the user288// may request that it be omitted. It is added as necessary289// by the Formatter.290} else {291int t = Math.min(nDigits - exp, precision);292mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));293System.arraycopy(digits, 0, mantissa, startIndex, exp);294// Do not append ".0" for formatted floats since the user295// may request that it be omitted. It is added as necessary296// by the Formatter.297if (t > 0) {298mantissa[startIndex + exp] = '.';299System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);300}301}302} else if (exp <= 0) {303int zeros = Math.max(0, Math.min(-exp, precision));304int t = Math.max(0, Math.min(nDigits, precision + exp));305// write '0' s before the significant digits306if (zeros > 0) {307mantissa = create(isNegative, zeros + 2 + t);308mantissa[startIndex] = '0';309mantissa[startIndex+1] = '.';310Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');311if (t > 0) {312// copy only when significant digits are within the precision313System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);314}315} else if (t > 0) {316mantissa = create(isNegative, zeros + 2 + t);317mantissa[startIndex] = '0';318mantissa[startIndex + 1] = '.';319// copy only when significant digits are within the precision320System.arraycopy(digits, 0, mantissa, startIndex + 2, t);321} else {322this.mantissa = create(isNegative, 1);323this.mantissa[startIndex] = '0';324}325}326}327328/**329* Fills mantissa and exponent char arrays for SCIENTIFIC format.330*/331private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {332int startIndex = isNegative ? 1 : 0;333int t = Math.max(0, Math.min(nDigits - 1, precision));334if (t > 0) {335mantissa = create(isNegative, t + 2);336mantissa[startIndex] = digits[0];337mantissa[startIndex + 1] = '.';338System.arraycopy(digits, 1, mantissa, startIndex + 2, t);339} else {340mantissa = create(isNegative, 1);341mantissa[startIndex] = digits[0];342}343char expSign;344int e;345if (exp <= 0) {346expSign = '-';347e = -exp + 1;348} else {349expSign = '+' ;350e = exp - 1;351}352// decExponent has 1, 2, or 3, digits353if (e <= 9) {354exponent = new char[] { expSign,355'0', (char) (e + '0') };356} else if (e <= 99) {357exponent = new char[] { expSign,358(char) (e / 10 + '0'), (char) (e % 10 + '0') };359} else {360char hiExpChar = (char) (e / 100 + '0');361e %= 100;362exponent = new char[] { expSign,363hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };364}365}366}367368369