• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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