Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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