Home | History | Annotate | Download | only in misc
      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 }
    350