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 
  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 }