1 /* 2 * Copyright (c) 2023, 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 /* 29 * This class provides support for the 'e', 'f' and 'g' conversions on double 30 * values with sign bit 0. 31 * It is worth noting that float values are converted to double values _before_ 32 * control reaches code in this class. 33 * 34 * It delegates the conversion to decimal to class DoubleToDecimal to get 35 * the decimal d selected by Double.toString(double) as a pair of integers 36 * f and e meeting d = f 10^e. 37 * It then rounds d to the appropriate number of digits, as per specification, 38 * and extracts the digits of both the significand and, where required, the 39 * exponent of the rounded value. 40 * 41 * Further processing like padding, sign, grouping, localization, etc., is the 42 * responsibility of the caller. 43 */ 44 public final class FormattedFPDecimal { 45 46 public static final char SCIENTIFIC = 'e'; 47 public static final char PLAIN = 'f'; 48 public static final char GENERAL = 'g'; 49 50 private long f; 51 private int e; // normalized to 0 when f = 0 52 private int n; 53 private char[] digits; // ... and often the decimal separator as well 54 private char[] exp; // [+-][e]ee, that is, sign and minimum 2 digits 55 FormattedFPDecimal()56 private FormattedFPDecimal() { 57 } 58 valueOf(double v, int prec, char form)59 public static FormattedFPDecimal valueOf(double v, int prec, char form) { 60 FormattedFPDecimal fd = new FormattedFPDecimal(); 61 DoubleToDecimal.split(v, fd); 62 return switch (form) { 63 case SCIENTIFIC -> fd.scientific(prec); 64 case PLAIN -> fd.plain(prec); 65 case GENERAL -> fd.general(prec); 66 default -> throw new IllegalArgumentException( 67 String.format("unsupported form '%c'", form) 68 ); 69 }; 70 } 71 set(long f, int e, int n)72 public void set(long f, int e, int n) { 73 /* Initially, n = 0 if f = 0, and 10^{n-1} <= f < 10^n if f != 0 */ 74 this.f = f; 75 this.e = e; 76 this.n = n; 77 } 78 getExponent()79 public char[] getExponent() { 80 return exp; 81 } 82 getMantissa()83 public char[] getMantissa() { 84 return digits; 85 } 86 getExponentRounded()87 public int getExponentRounded() { 88 return n + e - 1; 89 } 90 plain(int prec)91 private FormattedFPDecimal plain(int prec) { 92 /* 93 * Rounding d = f 10^e to prec digits in plain mode means the same 94 * as rounding it to the p = n + e + prec most significand digits of d, 95 * with the understanding that p < 0 cuts off all its digits. 96 */ 97 round(n + e + (long) prec); // n + e is well inside the int range 98 return plainChars(); 99 } 100 plainChars()101 private FormattedFPDecimal plainChars() { 102 if (e >= 0) { 103 plainCharsPureInteger(); 104 } else if (n + e > 0) { 105 plainCharsMixed(); 106 } else { 107 plainCharsPureFraction(); 108 } 109 return this; 110 } 111 plainCharsPureInteger()112 private void plainCharsPureInteger() { 113 digits = new char[n + e]; 114 fillWithZeros(n, n + e); 115 fillWithDigits(f, 0, n); 116 } 117 plainCharsMixed()118 private void plainCharsMixed() { 119 digits = new char[n + 1]; 120 long x = fillWithDigits(f, n + 1 + e, n + 1); 121 digits[n + e] = '.'; 122 fillWithDigits(x, 0, n + e); 123 } 124 plainCharsPureFraction()125 private void plainCharsPureFraction() { 126 digits = new char[2 - e]; 127 long x = f; 128 fillWithDigits(x, 2 - e - n, 2 - e); 129 fillWithZeros(0, 2 - e - n); 130 digits[1] = '.'; 131 } 132 scientific(int prec)133 private FormattedFPDecimal scientific(int prec) { 134 /* 135 * Rounding d = f 10^e to prec digits in scientific mode means the same 136 * as rounding it to the p = prec + 1 most significand digits of d. 137 */ 138 round(prec + 1L); 139 return scientificChars(prec); 140 } 141 scientificChars(int prec)142 private FormattedFPDecimal scientificChars(int prec) { 143 if (prec != 0) { 144 scientificCharsWithFraction(); 145 } else { 146 scientificCharsNoFraction(); 147 } 148 expChars(); 149 return this; 150 } 151 scientificCharsWithFraction()152 private void scientificCharsWithFraction() { 153 digits = new char[1 + n]; // room for leading digit and for '.' 154 long x = fillWithDigits(f, 2, 1 + n); 155 digits[1] = '.'; 156 digits[0] = toDigit(x); 157 } 158 scientificCharsNoFraction()159 private void scientificCharsNoFraction() { 160 digits = new char[1]; 161 digits[0] = toDigit(f); 162 } 163 general(int prec)164 private FormattedFPDecimal general(int prec) { 165 /* 166 * Rounding d = f 10^e to prec digits in general mode means the same 167 * as rounding it to the p = prec most significand digits of d, and then 168 * deciding whether to format it in plain or scientific mode, depending 169 * on the rounded value. 170 */ 171 round(prec); 172 int er = getExponentRounded(); 173 if (-4 <= er && er < prec) { 174 plainChars(); 175 } else { 176 scientificChars(prec - 1); 177 } 178 return this; 179 } 180 expChars()181 private void expChars() { 182 int er = getExponentRounded(); 183 int aer = Math.abs(er); 184 exp = new char[aer >= 100 ? 4 : 3]; 185 int q; 186 if (aer >= 100) { 187 q = aer / 10; 188 exp[3] = toDigit(aer - 10 * q); 189 aer = q; 190 } 191 q = aer / 10; 192 exp[2] = toDigit(aer - 10 * q); 193 exp[1] = toDigit(q); 194 exp[0] = er >= 0 ? '+' : '-'; 195 } 196 round(long pp)197 private void round(long pp) { 198 /* 199 * Let d = f 10^e, and let p shorten pp. 200 * This method rounds d to the p most significant digits. 201 * It does so by possibly modifying f, e and n. 202 * When f becomes 0, e and n are normalized to 0 and 1, resp. 203 * 204 * For any real x let 205 * r(x) = floor(x + 1/2) 206 * which is rounding to the closest integer, with ties rounded toward 207 * positive infinity. 208 * 209 * When f = 0 there's not much to say, except that this holds iff n = 0. 210 * 211 * Otherwise, since 212 * 10^{n-1} <= f < 10^n 213 * it follows that 214 * 10^{e+n-1} <= d < 10^{e+n} 215 * To round d to the most significant p digits, first scale d to the 216 * range [10^{p-1}, 10^p), cutoff the fractional digits by applying r, 217 * and finally scale back. 218 * To this end, first define 219 * ds = d 10^{p-e-n} 220 * which ensures 221 * 10^{p-1} <= ds < 10^p 222 * 223 * Now, if p < 0 (that is, if p <= -1) then 224 * ds < 10^p <= 10^{-1} < 1/2 225 * so that 226 * r(ds) = 0 227 * Thus, rounding d to p < 0 digits leads to 0. 228 */ 229 if (n == 0 || pp < 0) { 230 f = 0; 231 e = 0; 232 n = 1; 233 return; 234 } 235 236 /* 237 * Further, if p >= n then 238 * ds = f 10^e 10^{p-e-n} = f 10^{p-n} 239 * which shows that ds is an integer, so r(ds) = ds. That is, 240 * rounding to p >= n digits leads to a result equal to d. 241 */ 242 if (pp >= n) { // no rounding needed 243 return; 244 } 245 246 /* 247 * Finally, 0 <= p < n. When p = 0 it follows that 248 * 10^{-1} <= ds < 1 249 * 0 <= f' = r(ds) <= 1 250 * that is, f' is either 0 or 1. 251 * 252 * Otherwise 253 * 10^{p-1} <= ds < 10^p 254 * 1 <= 10^{p-1} <= f' = r(ds) <= 10^p 255 * Note that f' = 10^p is a possible outcome. 256 * 257 * Scale back, where e' = e + n - p 258 * d' = f' 10^{e+n-p} = f' 10^e', with 10^{e+n-1} <= d' <= 10^{e+n} 259 * 260 * Since n > p, f' can be computed in integer arithmetic as follows, 261 * where / denotes division in the real numbers: 262 * f' = r(ds) = r(f 10^{p-n}) = r(f / 10^{n-p}) 263 * = floor(f / 10^{n-p} + 1/2) 264 * = floor((f + 10^{n-p}/2) / 10^{n-p}) 265 */ 266 int p = (int) pp; // 0 <= pp < n, safe cast 267 e += n - p; // new e is well inside the int range 268 long pow10 = MathUtils.pow10(n - p); 269 f = (f + (pow10 >> 1)) / pow10; 270 if (p == 0) { 271 n = 1; 272 if (f == 0) { 273 e = 0; 274 } 275 return; 276 } 277 278 n = p; 279 if (f == MathUtils.pow10(p)) { 280 /* 281 * f is n + 1 digits long. 282 * Absorb one trailing zero into e and reduce f accordingly. 283 */ 284 f /= 10; 285 e += 1; 286 } 287 } 288 289 /* 290 * Fills the digits section with indices in [from, to) with the lower 291 * to - from digits of x (as chars), while stripping them away from x. 292 * Returns the stripped x. 293 */ fillWithDigits(long x, int from, int to)294 private long fillWithDigits(long x, int from, int to) { 295 while (to > from) { 296 long q = x / 10; 297 digits[--to] = toDigit(x - q * 10); 298 x = q; 299 } 300 return x; 301 } 302 303 /* 304 * Fills the digits section with indices in [from, to) with '0'. 305 */ fillWithZeros(int from, int to)306 private void fillWithZeros(int from, int to) { 307 while (to > from) { 308 digits[--to] = '0'; 309 } 310 } 311 toDigit(long d)312 private static char toDigit(long d) { 313 return toDigit((int) d); 314 } 315 toDigit(int d)316 private static char toDigit(int d) { 317 return (char) (d + '0'); 318 } 319 320 } 321