Skip to content

Commit f696785

Browse files
committed
8300869: Make use of the Double.toString(double) algorithm in java.util.Formatter
Reviewed-by: darcy, naoto
1 parent cf6b9eb commit f696785

File tree

11 files changed

+564
-412
lines changed

11 files changed

+564
-412
lines changed

src/java.base/share/classes/java/util/Formatter.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
import java.time.temporal.UnsupportedTemporalTypeException;
6262

6363
import jdk.internal.math.DoubleConsts;
64-
import jdk.internal.math.FormattedFloatingDecimal;
64+
import jdk.internal.math.FormattedFPDecimal;
6565
import sun.util.locale.provider.LocaleProviderAdapter;
6666
import sun.util.locale.provider.ResourceBundleBasedAdapter;
6767

@@ -1260,6 +1260,9 @@
12601260
* id="scientific">computerized scientific notation</a>. The <a
12611261
* href="#L10nAlgorithm">localization algorithm</a> is applied.
12621262
*
1263+
* <p> A {@code float} or {@link Float} argument is first converted to
1264+
* {@code double} or {@link Double}, without loss of precision.
1265+
*
12631266
* <p> The formatting of the magnitude <i>m</i> depends upon its value.
12641267
*
12651268
* <p> If <i>m</i> is NaN or infinite, the literal strings "NaN" or
@@ -1291,8 +1294,8 @@
12911294
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
12921295
* specified then the default value is {@code 6}. If the precision is less
12931296
* than the number of digits which would appear after the decimal point in
1294-
* the string returned by {@link Float#toString(float)} or {@link
1295-
* Double#toString(double)} respectively, then the value will be rounded
1297+
* the string returned by {@link
1298+
* Double#toString(double)}, then the value will be rounded
12961299
* using the {@linkplain java.math.RoundingMode#HALF_UP round half up
12971300
* algorithm}. Otherwise, zeros may be appended to reach the precision.
12981301
* For a canonical representation of the value, use {@link
@@ -1342,6 +1345,9 @@
13421345
* format</a>. The <a href="#L10nAlgorithm">localization algorithm</a> is
13431346
* applied.
13441347
*
1348+
* <p> A {@code float} or {@link Float} argument is first converted to
1349+
* {@code double} or {@link Double}, without loss of precision.
1350+
*
13451351
* <p> The result is a string that represents the sign and magnitude
13461352
* (absolute value) of the argument. The formatting of the sign is
13471353
* described in the <a href="#L10nAlgorithm">localization
@@ -1360,8 +1366,8 @@
13601366
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
13611367
* specified then the default value is {@code 6}. If the precision is less
13621368
* than the number of digits which would appear after the decimal point in
1363-
* the string returned by {@link Float#toString(float)} or {@link
1364-
* Double#toString(double)} respectively, then the value will be rounded
1369+
* the string returned by {@link
1370+
* Double#toString(double)}, then the value will be rounded
13651371
* using the {@linkplain java.math.RoundingMode#HALF_UP round half up
13661372
* algorithm}. Otherwise, zeros may be appended to reach the precision.
13671373
* For a canonical representation of the value, use {@link
@@ -3512,19 +3518,16 @@ private void print(Formatter fmt, double value, Locale l) throws IOException {
35123518
appendJustified(fmt.a, sb);
35133519
}
35143520

3515-
// !Double.isInfinite(value) && !Double.isNaN(value)
3521+
// !Double.isInfinite(value) && !Double.isNaN(value) && value sign bit is 0
35163522
private void print(Formatter fmt, StringBuilder sb, double value, Locale l,
3517-
int flags, char c, int precision, boolean neg)
3518-
throws IOException
3519-
{
3523+
int flags, char c, int precision, boolean neg) {
35203524
if (c == Conversion.SCIENTIFIC) {
3521-
// Create a new FormattedFloatingDecimal with the desired
3525+
// Create a new FormattedFPDecimal with the desired
35223526
// precision.
35233527
int prec = (precision == -1 ? 6 : precision);
35243528

3525-
FormattedFloatingDecimal fd
3526-
= FormattedFloatingDecimal.valueOf(value, prec,
3527-
FormattedFloatingDecimal.Form.SCIENTIFIC);
3529+
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
3530+
value, prec, FormattedFPDecimal.SCIENTIFIC);
35283531

35293532
StringBuilder mant = new StringBuilder().append(fd.getMantissa());
35303533
addZeros(mant, prec);
@@ -3552,13 +3555,12 @@ private void print(Formatter fmt, StringBuilder sb, double value, Locale l,
35523555

35533556
localizedMagnitudeExp(fmt, sb, exp, 1, l);
35543557
} else if (c == Conversion.DECIMAL_FLOAT) {
3555-
// Create a new FormattedFloatingDecimal with the desired
3558+
// Create a new FormattedFPDecimal with the desired
35563559
// precision.
35573560
int prec = (precision == -1 ? 6 : precision);
35583561

3559-
FormattedFloatingDecimal fd
3560-
= FormattedFloatingDecimal.valueOf(value, prec,
3561-
FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3562+
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
3563+
value, prec, FormattedFPDecimal.PLAIN);
35623564

35633565
StringBuilder mant = new StringBuilder().append(fd.getMantissa());
35643566
addZeros(mant, prec);
@@ -3587,9 +3589,8 @@ else if (precision == 0)
35873589
mant.append('0');
35883590
expRounded = 0;
35893591
} else {
3590-
FormattedFloatingDecimal fd
3591-
= FormattedFloatingDecimal.valueOf(value, prec,
3592-
FormattedFloatingDecimal.Form.GENERAL);
3592+
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
3593+
value, prec, FormattedFPDecimal.GENERAL);
35933594
exp = fd.getExponent();
35943595
mant.append(fd.getMantissa());
35953596
expRounded = fd.getExponentRounded();

src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
3535
/**
3636
* This class exposes a method to render a {@code double} as a string.
3737
*/
38-
final public class DoubleToDecimal {
38+
public final class DoubleToDecimal {
3939
/*
4040
* For full details about this code see the following references:
4141
*
@@ -110,12 +110,13 @@ final public class DoubleToDecimal {
110110
*/
111111
public static final int MAX_CHARS = H + 7;
112112

113-
private final byte[] bytes = new byte[MAX_CHARS];
113+
private final byte[] bytes;
114114

115115
/* Index into bytes of rightmost valid character */
116116
private int index;
117117

118-
private DoubleToDecimal() {
118+
private DoubleToDecimal(boolean noChars) {
119+
bytes = noChars ? null : new byte[MAX_CHARS];
119120
}
120121

121122
/**
@@ -127,7 +128,28 @@ private DoubleToDecimal() {
127128
* @see Double#toString(double)
128129
*/
129130
public static String toString(double v) {
130-
return new DoubleToDecimal().toDecimalString(v);
131+
return new DoubleToDecimal(false).toDecimalString(v);
132+
}
133+
134+
/**
135+
* Splits the decimal <i>d</i> described in
136+
* {@link Double#toString(double)} in integers <i>f</i> and <i>e</i>
137+
* such that <i>d</i> = <i>f</i> 10<sup><i>e</i></sup>.
138+
*
139+
* <p>Further, determines integer <i>n</i> such that <i>n</i> = 0 when
140+
* <i>f</i> = 0, and
141+
* 10<sup><i>n</i>-1</sup> &le; <i>f</i> &lt; 10<sup><i>n</i></sup>
142+
* otherwise.
143+
*
144+
* <p>The argument {@code v} is assumed to be a positive finite value or
145+
* positive zero.
146+
* Further, {@code fd} must not be {@code null}.
147+
*
148+
* @param v the finite {@code double} to be split.
149+
* @param fd the object that will carry <i>f</i>, <i>e</i>, and <i>n</i>.
150+
*/
151+
public static void split(double v, FormattedFPDecimal fd) {
152+
new DoubleToDecimal(true).toDecimal(v, fd);
131153
}
132154

133155
/**
@@ -143,11 +165,11 @@ public static String toString(double v) {
143165
*/
144166
public static Appendable appendTo(double v, Appendable app)
145167
throws IOException {
146-
return new DoubleToDecimal().appendDecimalTo(v, app);
168+
return new DoubleToDecimal(false).appendDecimalTo(v, app);
147169
}
148170

149171
private String toDecimalString(double v) {
150-
return switch (toDecimal(v)) {
172+
return switch (toDecimal(v, null)) {
151173
case NON_SPECIAL -> charsToString();
152174
case PLUS_ZERO -> "0.0";
153175
case MINUS_ZERO -> "-0.0";
@@ -159,7 +181,7 @@ private String toDecimalString(double v) {
159181

160182
private Appendable appendDecimalTo(double v, Appendable app)
161183
throws IOException {
162-
switch (toDecimal(v)) {
184+
switch (toDecimal(v, null)) {
163185
case NON_SPECIAL:
164186
char[] chars = new char[index + 1];
165187
for (int i = 0; i < chars.length; ++i) {
@@ -191,7 +213,7 @@ private Appendable appendDecimalTo(double v, Appendable app)
191213
* MINUS_INF iff v is NEGATIVE_INFINITY
192214
* NAN iff v is NaN
193215
*/
194-
private int toDecimal(double v) {
216+
private int toDecimal(double v, FormattedFPDecimal fd) {
195217
/*
196218
* For full details see references [2] and [1].
197219
*
@@ -207,6 +229,10 @@ private int toDecimal(double v) {
207229
if (bq < BQ_MASK) {
208230
index = -1;
209231
if (bits < 0) {
232+
/*
233+
* fd != null implies bytes == null and bits >= 0
234+
* Thus, when fd != null, control never reaches here.
235+
*/
210236
append('-');
211237
}
212238
if (bq != 0) {
@@ -217,16 +243,16 @@ private int toDecimal(double v) {
217243
if (0 < mq & mq < P) {
218244
long f = c >> mq;
219245
if (f << mq == c) {
220-
return toChars(f, 0);
246+
return toChars(f, 0, fd);
221247
}
222248
}
223-
return toDecimal(-mq, c, 0);
249+
return toDecimal(-mq, c, 0, fd);
224250
}
225251
if (t != 0) {
226252
/* subnormal value */
227253
return t < C_TINY
228-
? toDecimal(Q_MIN, 10 * t, -1)
229-
: toDecimal(Q_MIN, t, 0);
254+
? toDecimal(Q_MIN, 10 * t, -1, fd)
255+
: toDecimal(Q_MIN, t, 0, fd);
230256
}
231257
return bits == 0 ? PLUS_ZERO : MINUS_ZERO;
232258
}
@@ -236,7 +262,7 @@ private int toDecimal(double v) {
236262
return bits > 0 ? PLUS_INF : MINUS_INF;
237263
}
238264

239-
private int toDecimal(int q, long c, int dk) {
265+
private int toDecimal(int q, long c, int dk, FormattedFPDecimal fd) {
240266
/*
241267
* The skeleton corresponds to figure 7 of [1].
242268
* The efficient computations are those summarized in figure 9.
@@ -301,7 +327,7 @@ private int toDecimal(int q, long c, int dk) {
301327
boolean upin = vbl + out <= sp10 << 2;
302328
boolean wpin = (tp10 << 2) + out <= vbr;
303329
if (upin != wpin) {
304-
return toChars(upin ? sp10 : tp10, k);
330+
return toChars(upin ? sp10 : tp10, k, fd);
305331
}
306332
}
307333

@@ -316,14 +342,14 @@ private int toDecimal(int q, long c, int dk) {
316342
boolean win = (t << 2) + out <= vbr;
317343
if (uin != win) {
318344
/* Exactly one of u or w lies in Rv */
319-
return toChars(uin ? s : t, k + dk);
345+
return toChars(uin ? s : t, k + dk, fd);
320346
}
321347
/*
322348
* Both u and w lie in Rv: determine the one closest to v.
323349
* See section 9.3 of [1].
324350
*/
325351
long cmp = vb - (s + t << 1);
326-
return toChars(cmp < 0 || cmp == 0 && (s & 0x1) == 0 ? s : t, k + dk);
352+
return toChars(cmp < 0 || cmp == 0 && (s & 0x1) == 0 ? s : t, k + dk, fd);
327353
}
328354

329355
/*
@@ -342,7 +368,7 @@ private static long rop(long g1, long g0, long cp) {
342368
/*
343369
* Formats the decimal f 10^e.
344370
*/
345-
private int toChars(long f, int e) {
371+
private int toChars(long f, int e, FormattedFPDecimal fd) {
346372
/*
347373
* For details not discussed here see section 10 of [1].
348374
*
@@ -353,6 +379,10 @@ private int toChars(long f, int e) {
353379
if (f >= pow10(len)) {
354380
len += 1;
355381
}
382+
if (fd != null) {
383+
fd.set(f, e, len);
384+
return NON_SPECIAL;
385+
}
356386

357387
/*
358388
* Let fp and ep be the original f and e, respectively.

0 commit comments

Comments
 (0)