jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java
changeset 34858 ec69df775846
parent 34857 14d1224cfed3
parent 34852 bd26599f2098
child 34859 4379223f8806
equal deleted inserted replaced
34857:14d1224cfed3 34858:ec69df775846
     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 }