Skip to content

Commit

Permalink
8300869: Make use of the Double.toString(double) algorithm in java.ut…
Browse files Browse the repository at this point in the history
…il.Formatter

Reviewed-by: darcy, naoto
  • Loading branch information
rgiulietti committed Feb 2, 2023
1 parent cf6b9eb commit f696785
Show file tree
Hide file tree
Showing 11 changed files with 564 additions and 412 deletions.
41 changes: 21 additions & 20 deletions src/java.base/share/classes/java/util/Formatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
import java.time.temporal.UnsupportedTemporalTypeException;

import jdk.internal.math.DoubleConsts;
import jdk.internal.math.FormattedFloatingDecimal;
import jdk.internal.math.FormattedFPDecimal;
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.ResourceBundleBasedAdapter;

Expand Down Expand Up @@ -1260,6 +1260,9 @@
* id="scientific">computerized scientific notation</a>. The <a
* href="#L10nAlgorithm">localization algorithm</a> is applied.
*
* <p> A {@code float} or {@link Float} argument is first converted to
* {@code double} or {@link Double}, without loss of precision.
*
* <p> The formatting of the magnitude <i>m</i> depends upon its value.
*
* <p> If <i>m</i> is NaN or infinite, the literal strings "NaN" or
Expand Down Expand Up @@ -1291,8 +1294,8 @@
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
* specified then the default value is {@code 6}. If the precision is less
* than the number of digits which would appear after the decimal point in
* the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
* the string returned by {@link
* Double#toString(double)}, then the value will be rounded
* using the {@linkplain java.math.RoundingMode#HALF_UP round half up
* algorithm}. Otherwise, zeros may be appended to reach the precision.
* For a canonical representation of the value, use {@link
Expand Down Expand Up @@ -1342,6 +1345,9 @@
* format</a>. The <a href="#L10nAlgorithm">localization algorithm</a> is
* applied.
*
* <p> A {@code float} or {@link Float} argument is first converted to
* {@code double} or {@link Double}, without loss of precision.
*
* <p> The result is a string that represents the sign and magnitude
* (absolute value) of the argument. The formatting of the sign is
* described in the <a href="#L10nAlgorithm">localization
Expand All @@ -1360,8 +1366,8 @@
* <i>m</i> or <i>a</i> is equal to the precision. If the precision is not
* specified then the default value is {@code 6}. If the precision is less
* than the number of digits which would appear after the decimal point in
* the string returned by {@link Float#toString(float)} or {@link
* Double#toString(double)} respectively, then the value will be rounded
* the string returned by {@link
* Double#toString(double)}, then the value will be rounded
* using the {@linkplain java.math.RoundingMode#HALF_UP round half up
* algorithm}. Otherwise, zeros may be appended to reach the precision.
* For a canonical representation of the value, use {@link
Expand Down Expand Up @@ -3512,19 +3518,16 @@ private void print(Formatter fmt, double value, Locale l) throws IOException {
appendJustified(fmt.a, sb);
}

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

FormattedFloatingDecimal fd
= FormattedFloatingDecimal.valueOf(value, prec,
FormattedFloatingDecimal.Form.SCIENTIFIC);
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
value, prec, FormattedFPDecimal.SCIENTIFIC);

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

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

FormattedFloatingDecimal fd
= FormattedFloatingDecimal.valueOf(value, prec,
FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
value, prec, FormattedFPDecimal.PLAIN);

StringBuilder mant = new StringBuilder().append(fd.getMantissa());
addZeros(mant, prec);
Expand Down Expand Up @@ -3587,9 +3589,8 @@ else if (precision == 0)
mant.append('0');
expRounded = 0;
} else {
FormattedFloatingDecimal fd
= FormattedFloatingDecimal.valueOf(value, prec,
FormattedFloatingDecimal.Form.GENERAL);
FormattedFPDecimal fd = FormattedFPDecimal.valueOf(
value, prec, FormattedFPDecimal.GENERAL);
exp = fd.getExponent();
mant.append(fd.getMantissa());
expRounded = fd.getExponentRounded();
Expand Down
66 changes: 48 additions & 18 deletions src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -35,7 +35,7 @@
/**
* This class exposes a method to render a {@code double} as a string.
*/
final public class DoubleToDecimal {
public final class DoubleToDecimal {
/*
* For full details about this code see the following references:
*
Expand Down Expand Up @@ -110,12 +110,13 @@ final public class DoubleToDecimal {
*/
public static final int MAX_CHARS = H + 7;

private final byte[] bytes = new byte[MAX_CHARS];
private final byte[] bytes;

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

private DoubleToDecimal() {
private DoubleToDecimal(boolean noChars) {
bytes = noChars ? null : new byte[MAX_CHARS];
}

/**
Expand All @@ -127,7 +128,28 @@ private DoubleToDecimal() {
* @see Double#toString(double)
*/
public static String toString(double v) {
return new DoubleToDecimal().toDecimalString(v);
return new DoubleToDecimal(false).toDecimalString(v);
}

/**
* Splits the decimal <i>d</i> described in
* {@link Double#toString(double)} in integers <i>f</i> and <i>e</i>
* such that <i>d</i> = <i>f</i> 10<sup><i>e</i></sup>.
*
* <p>Further, determines integer <i>n</i> such that <i>n</i> = 0 when
* <i>f</i> = 0, and
* 10<sup><i>n</i>-1</sup> &le; <i>f</i> &lt; 10<sup><i>n</i></sup>
* otherwise.
*
* <p>The argument {@code v} is assumed to be a positive finite value or
* positive zero.
* Further, {@code fd} must not be {@code null}.
*
* @param v the finite {@code double} to be split.
* @param fd the object that will carry <i>f</i>, <i>e</i>, and <i>n</i>.
*/
public static void split(double v, FormattedFPDecimal fd) {
new DoubleToDecimal(true).toDecimal(v, fd);
}

/**
Expand All @@ -143,11 +165,11 @@ public static String toString(double v) {
*/
public static Appendable appendTo(double v, Appendable app)
throws IOException {
return new DoubleToDecimal().appendDecimalTo(v, app);
return new DoubleToDecimal(false).appendDecimalTo(v, app);
}

private String toDecimalString(double v) {
return switch (toDecimal(v)) {
return switch (toDecimal(v, null)) {
case NON_SPECIAL -> charsToString();
case PLUS_ZERO -> "0.0";
case MINUS_ZERO -> "-0.0";
Expand All @@ -159,7 +181,7 @@ private String toDecimalString(double v) {

private Appendable appendDecimalTo(double v, Appendable app)
throws IOException {
switch (toDecimal(v)) {
switch (toDecimal(v, null)) {
case NON_SPECIAL:
char[] chars = new char[index + 1];
for (int i = 0; i < chars.length; ++i) {
Expand Down Expand Up @@ -191,7 +213,7 @@ private Appendable appendDecimalTo(double v, Appendable app)
* MINUS_INF iff v is NEGATIVE_INFINITY
* NAN iff v is NaN
*/
private int toDecimal(double v) {
private int toDecimal(double v, FormattedFPDecimal fd) {
/*
* For full details see references [2] and [1].
*
Expand All @@ -207,6 +229,10 @@ private int toDecimal(double v) {
if (bq < BQ_MASK) {
index = -1;
if (bits < 0) {
/*
* fd != null implies bytes == null and bits >= 0
* Thus, when fd != null, control never reaches here.
*/
append('-');
}
if (bq != 0) {
Expand All @@ -217,16 +243,16 @@ private int toDecimal(double v) {
if (0 < mq & mq < P) {
long f = c >> mq;
if (f << mq == c) {
return toChars(f, 0);
return toChars(f, 0, fd);
}
}
return toDecimal(-mq, c, 0);
return toDecimal(-mq, c, 0, fd);
}
if (t != 0) {
/* subnormal value */
return t < C_TINY
? toDecimal(Q_MIN, 10 * t, -1)
: toDecimal(Q_MIN, t, 0);
? toDecimal(Q_MIN, 10 * t, -1, fd)
: toDecimal(Q_MIN, t, 0, fd);
}
return bits == 0 ? PLUS_ZERO : MINUS_ZERO;
}
Expand All @@ -236,7 +262,7 @@ private int toDecimal(double v) {
return bits > 0 ? PLUS_INF : MINUS_INF;
}

private int toDecimal(int q, long c, int dk) {
private int toDecimal(int q, long c, int dk, FormattedFPDecimal fd) {
/*
* The skeleton corresponds to figure 7 of [1].
* The efficient computations are those summarized in figure 9.
Expand Down Expand Up @@ -301,7 +327,7 @@ private int toDecimal(int q, long c, int dk) {
boolean upin = vbl + out <= sp10 << 2;
boolean wpin = (tp10 << 2) + out <= vbr;
if (upin != wpin) {
return toChars(upin ? sp10 : tp10, k);
return toChars(upin ? sp10 : tp10, k, fd);
}
}

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

/*
Expand All @@ -342,7 +368,7 @@ private static long rop(long g1, long g0, long cp) {
/*
* Formats the decimal f 10^e.
*/
private int toChars(long f, int e) {
private int toChars(long f, int e, FormattedFPDecimal fd) {
/*
* For details not discussed here see section 10 of [1].
*
Expand All @@ -353,6 +379,10 @@ private int toChars(long f, int e) {
if (f >= pow10(len)) {
len += 1;
}
if (fd != null) {
fd.set(f, e, len);
return NON_SPECIAL;
}

/*
* Let fp and ep be the original f and e, respectively.
Expand Down
Loading

1 comment on commit f696785

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.