8300869: Make use of the Double.toString(double) algorithm in java.util.Formatter
Reviewed-by: darcy, naoto
This commit is contained in:
parent
cf6b9eb8c8
commit
f696785fd3
src/java.base/share/classes
java/util
jdk/internal/math
test/jdk/java/util/Formatter
@ -61,7 +61,7 @@ import java.time.temporal.TemporalQueries;
|
||||
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;
|
||||
|
||||
@ -1260,6 +1260,9 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
||||
* 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
|
||||
@ -1291,8 +1294,8 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
||||
* <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
|
||||
@ -1342,6 +1345,9 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
||||
* 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
|
||||
@ -1360,8 +1366,8 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter;
|
||||
* <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
|
||||
@ -3512,19 +3518,16 @@ public final class Formatter implements Closeable, Flushable {
|
||||
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);
|
||||
@ -3552,13 +3555,12 @@ public final class Formatter implements Closeable, Flushable {
|
||||
|
||||
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);
|
||||
@ -3587,9 +3589,8 @@ public final class Formatter implements Closeable, Flushable {
|
||||
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();
|
||||
|
@ -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
|
||||
@ -35,7 +35,7 @@ import static jdk.internal.math.MathUtils.*;
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,7 +128,28 @@ final public class 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> ≤ <i>f</i> < 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,11 +165,11 @@ final public class DoubleToDecimal {
|
||||
*/
|
||||
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";
|
||||
@ -159,7 +181,7 @@ final public class DoubleToDecimal {
|
||||
|
||||
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) {
|
||||
@ -191,7 +213,7 @@ final public class DoubleToDecimal {
|
||||
* 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].
|
||||
*
|
||||
@ -207,6 +229,10 @@ final public class DoubleToDecimal {
|
||||
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) {
|
||||
@ -217,16 +243,16 @@ final public class DoubleToDecimal {
|
||||
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;
|
||||
}
|
||||
@ -236,7 +262,7 @@ final public class DoubleToDecimal {
|
||||
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.
|
||||
@ -301,7 +327,7 @@ final public class DoubleToDecimal {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,14 +342,14 @@ final public class DoubleToDecimal {
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -342,7 +368,7 @@ final public class DoubleToDecimal {
|
||||
/*
|
||||
* 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].
|
||||
*
|
||||
@ -353,6 +379,10 @@ final public class DoubleToDecimal {
|
||||
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.
|
||||
|
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.math;
|
||||
|
||||
/*
|
||||
* This class provides support for the 'e', 'f' and 'g' conversions on double
|
||||
* values with sign bit 0.
|
||||
* It is worth noting that float values are converted to double values _before_
|
||||
* control reaches code in this class.
|
||||
*
|
||||
* It delegates the conversion to decimal to class DoubleToDecimal to get
|
||||
* the decimal d selected by Double.toString(double) as a pair of integers
|
||||
* f and e meeting d = f 10^e.
|
||||
* It then rounds d to the appropriate number of digits, as per specification,
|
||||
* and extracts the digits of both the significand and, where required, the
|
||||
* exponent of the rounded value.
|
||||
*
|
||||
* Further processing like padding, sign, grouping, localization, etc., is the
|
||||
* responsibility of the caller.
|
||||
*/
|
||||
public final class FormattedFPDecimal {
|
||||
|
||||
public static final char SCIENTIFIC = 'e';
|
||||
public static final char PLAIN = 'f';
|
||||
public static final char GENERAL = 'g';
|
||||
|
||||
private long f;
|
||||
private int e; // normalized to 0 when f = 0
|
||||
private int n;
|
||||
private char[] digits; // ... and often the decimal separator as well
|
||||
private char[] exp; // [+-][e]ee, that is, sign and minimum 2 digits
|
||||
|
||||
private FormattedFPDecimal() {
|
||||
}
|
||||
|
||||
public static FormattedFPDecimal valueOf(double v, int prec, char form) {
|
||||
FormattedFPDecimal fd = new FormattedFPDecimal();
|
||||
DoubleToDecimal.split(v, fd);
|
||||
return switch (form) {
|
||||
case SCIENTIFIC -> fd.scientific(prec);
|
||||
case PLAIN -> fd.plain(prec);
|
||||
case GENERAL -> fd.general(prec);
|
||||
default -> throw new IllegalArgumentException(
|
||||
String.format("unsupported form '%c'", form)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public void set(long f, int e, int n) {
|
||||
/* Initially, n = 0 if f = 0, and 10^{n-1} <= f < 10^n if f != 0 */
|
||||
this.f = f;
|
||||
this.e = e;
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
public char[] getExponent() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public char[] getMantissa() {
|
||||
return digits;
|
||||
}
|
||||
|
||||
public int getExponentRounded() {
|
||||
return n + e - 1;
|
||||
}
|
||||
|
||||
private FormattedFPDecimal plain(int prec) {
|
||||
/*
|
||||
* Rounding d = f 10^e to prec digits in plain mode means the same
|
||||
* as rounding it to the p = n + e + prec most significand digits of d,
|
||||
* with the understanding that p < 0 cuts off all its digits.
|
||||
*/
|
||||
round(n + e + (long) prec); // n + e is well inside the int range
|
||||
return plainChars();
|
||||
}
|
||||
|
||||
private FormattedFPDecimal plainChars() {
|
||||
if (e >= 0) {
|
||||
plainCharsPureInteger();
|
||||
} else if (n + e > 0) {
|
||||
plainCharsMixed();
|
||||
} else {
|
||||
plainCharsPureFraction();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void plainCharsPureInteger() {
|
||||
digits = new char[n + e];
|
||||
fillWithZeros(n, n + e);
|
||||
fillWithDigits(f, 0, n);
|
||||
}
|
||||
|
||||
private void plainCharsMixed() {
|
||||
digits = new char[n + 1];
|
||||
long x = fillWithDigits(f, n + 1 + e, n + 1);
|
||||
digits[n + e] = '.';
|
||||
fillWithDigits(x, 0, n + e);
|
||||
}
|
||||
|
||||
private void plainCharsPureFraction() {
|
||||
digits = new char[2 - e];
|
||||
long x = f;
|
||||
fillWithDigits(x, 2 - e - n, 2 - e);
|
||||
fillWithZeros(0, 2 - e - n);
|
||||
digits[1] = '.';
|
||||
}
|
||||
|
||||
private FormattedFPDecimal scientific(int prec) {
|
||||
/*
|
||||
* Rounding d = f 10^e to prec digits in scientific mode means the same
|
||||
* as rounding it to the p = prec + 1 most significand digits of d.
|
||||
*/
|
||||
round(prec + 1L);
|
||||
return scientificChars(prec);
|
||||
}
|
||||
|
||||
private FormattedFPDecimal scientificChars(int prec) {
|
||||
if (prec != 0) {
|
||||
scientificCharsWithFraction();
|
||||
} else {
|
||||
scientificCharsNoFraction();
|
||||
}
|
||||
expChars();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void scientificCharsWithFraction() {
|
||||
digits = new char[1 + n]; // room for leading digit and for '.'
|
||||
long x = fillWithDigits(f, 2, 1 + n);
|
||||
digits[1] = '.';
|
||||
digits[0] = toDigit(x);
|
||||
}
|
||||
|
||||
private void scientificCharsNoFraction() {
|
||||
digits = new char[1];
|
||||
digits[0] = toDigit(f);
|
||||
}
|
||||
|
||||
private FormattedFPDecimal general(int prec) {
|
||||
/*
|
||||
* Rounding d = f 10^e to prec digits in general mode means the same
|
||||
* as rounding it to the p = prec most significand digits of d, and then
|
||||
* deciding whether to format it in plain or scientific mode, depending
|
||||
* on the rounded value.
|
||||
*/
|
||||
round(prec);
|
||||
int er = getExponentRounded();
|
||||
if (-4 <= er && er < prec) {
|
||||
plainChars();
|
||||
} else {
|
||||
scientificChars(prec - 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void expChars() {
|
||||
int er = getExponentRounded();
|
||||
int aer = Math.abs(er);
|
||||
exp = new char[aer >= 100 ? 4 : 3];
|
||||
int q;
|
||||
if (aer >= 100) {
|
||||
q = aer / 10;
|
||||
exp[3] = toDigit(aer - 10 * q);
|
||||
aer = q;
|
||||
}
|
||||
q = aer / 10;
|
||||
exp[2] = toDigit(aer - 10 * q);
|
||||
exp[1] = toDigit(q);
|
||||
exp[0] = er >= 0 ? '+' : '-';
|
||||
}
|
||||
|
||||
private void round(long pp) {
|
||||
/*
|
||||
* Let d = f 10^e, and let p shorten pp.
|
||||
* This method rounds d to the p most significant digits.
|
||||
* It does so by possibly modifying f, e and n.
|
||||
* When f becomes 0, e and n are normalized to 0 and 1, resp.
|
||||
*
|
||||
* For any real x let
|
||||
* r(x) = floor(x + 1/2)
|
||||
* which is rounding to the closest integer, with ties rounded toward
|
||||
* positive infinity.
|
||||
*
|
||||
* When f = 0 there's not much to say, except that this holds iff n = 0.
|
||||
*
|
||||
* Otherwise, since
|
||||
* 10^{n-1} <= f < 10^n
|
||||
* it follows that
|
||||
* 10^{e+n-1} <= d < 10^{e+n}
|
||||
* To round d to the most significant p digits, first scale d to the
|
||||
* range [10^{p-1}, 10^p), cutoff the fractional digits by applying r,
|
||||
* and finally scale back.
|
||||
* To this end, first define
|
||||
* ds = d 10^{p-e-n}
|
||||
* which ensures
|
||||
* 10^{p-1} <= ds < 10^p
|
||||
*
|
||||
* Now, if p < 0 (that is, if p <= -1) then
|
||||
* ds < 10^p <= 10^{-1} < 1/2
|
||||
* so that
|
||||
* r(ds) = 0
|
||||
* Thus, rounding d to p < 0 digits leads to 0.
|
||||
*/
|
||||
if (n == 0 || pp < 0) {
|
||||
f = 0;
|
||||
e = 0;
|
||||
n = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Further, if p >= n then
|
||||
* ds = f 10^e 10^{p-e-n} = f 10^{p-n}
|
||||
* which shows that ds is an integer, so r(ds) = ds. That is,
|
||||
* rounding to p >= n digits leads to a result equal to d.
|
||||
*/
|
||||
if (pp >= n) { // no rounding needed
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, 0 <= p < n. When p = 0 it follows that
|
||||
* 10^{-1} <= ds < 1
|
||||
* 0 <= f' = r(ds) <= 1
|
||||
* that is, f' is either 0 or 1.
|
||||
*
|
||||
* Otherwise
|
||||
* 10^{p-1} <= ds < 10^p
|
||||
* 1 <= 10^{p-1} <= f' = r(ds) <= 10^p
|
||||
* Note that f' = 10^p is a possible outcome.
|
||||
*
|
||||
* Scale back, where e' = e + n - p
|
||||
* d' = f' 10^{e+n-p} = f' 10^e', with 10^{e+n-1} <= d' <= 10^{e+n}
|
||||
*
|
||||
* Since n > p, f' can be computed in integer arithmetic as follows,
|
||||
* where / denotes division in the real numbers:
|
||||
* f' = r(ds) = r(f 10^{p-n}) = r(f / 10^{n-p})
|
||||
* = floor(f / 10^{n-p} + 1/2)
|
||||
* = floor((f + 10^{n-p}/2) / 10^{n-p})
|
||||
*/
|
||||
int p = (int) pp; // 0 <= pp < n, safe cast
|
||||
e += n - p; // new e is well inside the int range
|
||||
long pow10 = MathUtils.pow10(n - p);
|
||||
f = (f + (pow10 >> 1)) / pow10;
|
||||
if (p == 0) {
|
||||
n = 1;
|
||||
if (f == 0) {
|
||||
e = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
n = p;
|
||||
if (f == MathUtils.pow10(p)) {
|
||||
/*
|
||||
* f is n + 1 digits long.
|
||||
* Absorb one trailing zero into e and reduce f accordingly.
|
||||
*/
|
||||
f /= 10;
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the digits section with indices in [from, to) with the lower
|
||||
* to - from digits of x (as chars), while stripping them away from x.
|
||||
* Returns the stripped x.
|
||||
*/
|
||||
private long fillWithDigits(long x, int from, int to) {
|
||||
while (to > from) {
|
||||
long q = x / 10;
|
||||
digits[--to] = toDigit(x - q * 10);
|
||||
x = q;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the digits section with indices in [from, to) with '0'.
|
||||
*/
|
||||
private void fillWithZeros(int from, int to) {
|
||||
while (to > from) {
|
||||
digits[--to] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
private static char toDigit(long d) {
|
||||
return toDigit((int) d);
|
||||
}
|
||||
|
||||
private static char toDigit(int d) {
|
||||
return (char) (d + '0');
|
||||
}
|
||||
|
||||
}
|
@ -1,367 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.math;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FormattedFloatingDecimal{
|
||||
|
||||
public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
|
||||
|
||||
|
||||
public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
|
||||
FloatingDecimal.BinaryToASCIIConverter fdConverter =
|
||||
FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
|
||||
return new FormattedFloatingDecimal(precision,form, fdConverter);
|
||||
}
|
||||
|
||||
private int decExponentRounded;
|
||||
private char[] mantissa;
|
||||
private char[] exponent;
|
||||
|
||||
private static final ThreadLocal<Object> threadLocalCharBuffer =
|
||||
new ThreadLocal<Object>() {
|
||||
@Override
|
||||
protected Object initialValue() {
|
||||
return new char[20];
|
||||
}
|
||||
};
|
||||
|
||||
private static char[] getBuffer(){
|
||||
return (char[]) threadLocalCharBuffer.get();
|
||||
}
|
||||
|
||||
private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
|
||||
if (fdConverter.isExceptional()) {
|
||||
this.mantissa = fdConverter.toJavaFormatString().toCharArray();
|
||||
this.exponent = null;
|
||||
return;
|
||||
}
|
||||
char[] digits = getBuffer();
|
||||
int nDigits = fdConverter.getDigits(digits);
|
||||
int decExp = fdConverter.getDecimalExponent();
|
||||
int exp;
|
||||
boolean isNegative = fdConverter.isNegative();
|
||||
switch (form) {
|
||||
case COMPATIBLE:
|
||||
exp = decExp;
|
||||
this.decExponentRounded = exp;
|
||||
fillCompatible(precision, digits, nDigits, exp, isNegative);
|
||||
break;
|
||||
case DECIMAL_FLOAT:
|
||||
exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
|
||||
fillDecimal(precision, digits, nDigits, exp, isNegative);
|
||||
this.decExponentRounded = exp;
|
||||
break;
|
||||
case SCIENTIFIC:
|
||||
exp = applyPrecision(decExp, digits, nDigits, precision + 1);
|
||||
fillScientific(precision, digits, nDigits, exp, isNegative);
|
||||
this.decExponentRounded = exp;
|
||||
break;
|
||||
case GENERAL:
|
||||
exp = applyPrecision(decExp, digits, nDigits, precision);
|
||||
// adjust precision to be the number of digits to right of decimal
|
||||
// the real exponent to be output is actually exp - 1, not exp
|
||||
if (exp - 1 < -4 || exp - 1 >= precision) {
|
||||
// form = Form.SCIENTIFIC;
|
||||
precision--;
|
||||
fillScientific(precision, digits, nDigits, exp, isNegative);
|
||||
} else {
|
||||
// form = Form.DECIMAL_FLOAT;
|
||||
precision = precision - exp;
|
||||
fillDecimal(precision, digits, nDigits, exp, isNegative);
|
||||
}
|
||||
this.decExponentRounded = exp;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the exponent after rounding has been done by applyPrecision
|
||||
public int getExponentRounded() {
|
||||
return decExponentRounded - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mantissa as a {@code char[]}. Note that the returned value
|
||||
* is a reference to the internal {@code char[]} containing the mantissa,
|
||||
* therefore code invoking this method should not pass the return value to
|
||||
* external code but should in that case make a copy.
|
||||
*
|
||||
* @return a reference to the internal {@code char[]} representing the
|
||||
* mantissa.
|
||||
*/
|
||||
public char[] getMantissa(){
|
||||
return mantissa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exponent as a {@code char[]}. Note that the returned value
|
||||
* is a reference to the internal {@code char[]} containing the exponent,
|
||||
* therefore code invoking this method should not pass the return value to
|
||||
* external code but should in that case make a copy.
|
||||
*
|
||||
* @return a reference to the internal {@code char[]} representing the
|
||||
* exponent.
|
||||
*/
|
||||
public char[] getExponent(){
|
||||
return exponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new decExp in case of overflow.
|
||||
*/
|
||||
private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
|
||||
if (prec >= nDigits || prec < 0) {
|
||||
// no rounding necessary
|
||||
return decExp;
|
||||
}
|
||||
if (prec == 0) {
|
||||
// only one digit (0 or 1) is returned because the precision
|
||||
// excludes all significant digits
|
||||
if (digits[0] >= '5') {
|
||||
digits[0] = '1';
|
||||
Arrays.fill(digits, 1, nDigits, '0');
|
||||
return decExp + 1;
|
||||
} else {
|
||||
Arrays.fill(digits, 0, nDigits, '0');
|
||||
return decExp;
|
||||
}
|
||||
}
|
||||
int q = digits[prec];
|
||||
if (q >= '5') {
|
||||
int i = prec;
|
||||
q = digits[--i];
|
||||
if ( q == '9' ) {
|
||||
while ( q == '9' && i > 0 ){
|
||||
q = digits[--i];
|
||||
}
|
||||
if ( q == '9' ){
|
||||
// carryout! High-order 1, rest 0s, larger exp.
|
||||
digits[0] = '1';
|
||||
Arrays.fill(digits, 1, nDigits, '0');
|
||||
return decExp+1;
|
||||
}
|
||||
}
|
||||
digits[i] = (char)(q + 1);
|
||||
Arrays.fill(digits, i+1, nDigits, '0');
|
||||
} else {
|
||||
Arrays.fill(digits, prec, nDigits, '0');
|
||||
}
|
||||
return decExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills mantissa and exponent char arrays for compatible format.
|
||||
*/
|
||||
private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
|
||||
int startIndex = isNegative ? 1 : 0;
|
||||
if (exp > 0 && exp < 8) {
|
||||
// print digits.digits.
|
||||
if (nDigits < exp) {
|
||||
int extraZeros = exp - nDigits;
|
||||
mantissa = create(isNegative, nDigits + extraZeros + 2);
|
||||
System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
|
||||
Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
|
||||
mantissa[startIndex + nDigits + extraZeros] = '.';
|
||||
mantissa[startIndex + nDigits + extraZeros+1] = '0';
|
||||
} else if (exp < nDigits) {
|
||||
int t = Math.min(nDigits - exp, precision);
|
||||
mantissa = create(isNegative, exp + 1 + t);
|
||||
System.arraycopy(digits, 0, mantissa, startIndex, exp);
|
||||
mantissa[startIndex + exp ] = '.';
|
||||
System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
|
||||
} else { // exp == digits.length
|
||||
mantissa = create(isNegative, nDigits + 2);
|
||||
System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
|
||||
mantissa[startIndex + nDigits ] = '.';
|
||||
mantissa[startIndex + nDigits +1] = '0';
|
||||
}
|
||||
} else if (exp <= 0 && exp > -3) {
|
||||
int zeros = Math.max(0, Math.min(-exp, precision));
|
||||
int t = Math.max(0, Math.min(nDigits, precision + exp));
|
||||
// write '0' s before the significant digits
|
||||
if (zeros > 0) {
|
||||
mantissa = create(isNegative, zeros + 2 + t);
|
||||
mantissa[startIndex] = '0';
|
||||
mantissa[startIndex+1] = '.';
|
||||
Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
|
||||
if (t > 0) {
|
||||
// copy only when significant digits are within the precision
|
||||
System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
|
||||
}
|
||||
} else if (t > 0) {
|
||||
mantissa = create(isNegative, zeros + 2 + t);
|
||||
mantissa[startIndex] = '0';
|
||||
mantissa[startIndex + 1] = '.';
|
||||
// copy only when significant digits are within the precision
|
||||
System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
|
||||
} else {
|
||||
this.mantissa = create(isNegative, 1);
|
||||
this.mantissa[startIndex] = '0';
|
||||
}
|
||||
} else {
|
||||
if (nDigits > 1) {
|
||||
mantissa = create(isNegative, nDigits + 1);
|
||||
mantissa[startIndex] = digits[0];
|
||||
mantissa[startIndex + 1] = '.';
|
||||
System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
|
||||
} else {
|
||||
mantissa = create(isNegative, 3);
|
||||
mantissa[startIndex] = digits[0];
|
||||
mantissa[startIndex + 1] = '.';
|
||||
mantissa[startIndex + 2] = '0';
|
||||
}
|
||||
int e, expStartIntex;
|
||||
boolean isNegExp = (exp <= 0);
|
||||
if (isNegExp) {
|
||||
e = -exp + 1;
|
||||
expStartIntex = 1;
|
||||
} else {
|
||||
e = exp - 1;
|
||||
expStartIntex = 0;
|
||||
}
|
||||
// decExponent has 1, 2, or 3, digits
|
||||
if (e <= 9) {
|
||||
exponent = create(isNegExp,1);
|
||||
exponent[expStartIntex] = (char) (e + '0');
|
||||
} else if (e <= 99) {
|
||||
exponent = create(isNegExp,2);
|
||||
exponent[expStartIntex] = (char) (e / 10 + '0');
|
||||
exponent[expStartIntex+1] = (char) (e % 10 + '0');
|
||||
} else {
|
||||
exponent = create(isNegExp,3);
|
||||
exponent[expStartIntex] = (char) (e / 100 + '0');
|
||||
e %= 100;
|
||||
exponent[expStartIntex+1] = (char) (e / 10 + '0');
|
||||
exponent[expStartIntex+2] = (char) (e % 10 + '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static char[] create(boolean isNegative, int size) {
|
||||
if(isNegative) {
|
||||
char[] r = new char[size +1];
|
||||
r[0] = '-';
|
||||
return r;
|
||||
} else {
|
||||
return new char[size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills mantissa char arrays for DECIMAL_FLOAT format.
|
||||
* Exponent should be equal to null.
|
||||
*/
|
||||
private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
|
||||
int startIndex = isNegative ? 1 : 0;
|
||||
if (exp > 0) {
|
||||
// print digits.digits.
|
||||
if (nDigits < exp) {
|
||||
mantissa = create(isNegative,exp);
|
||||
System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
|
||||
Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
|
||||
// Do not append ".0" for formatted floats since the user
|
||||
// may request that it be omitted. It is added as necessary
|
||||
// by the Formatter.
|
||||
} else {
|
||||
int t = Math.min(nDigits - exp, precision);
|
||||
mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
|
||||
System.arraycopy(digits, 0, mantissa, startIndex, exp);
|
||||
// Do not append ".0" for formatted floats since the user
|
||||
// may request that it be omitted. It is added as necessary
|
||||
// by the Formatter.
|
||||
if (t > 0) {
|
||||
mantissa[startIndex + exp] = '.';
|
||||
System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
|
||||
}
|
||||
}
|
||||
} else if (exp <= 0) {
|
||||
int zeros = Math.max(0, Math.min(-exp, precision));
|
||||
int t = Math.max(0, Math.min(nDigits, precision + exp));
|
||||
// write '0' s before the significant digits
|
||||
if (zeros > 0) {
|
||||
mantissa = create(isNegative, zeros + 2 + t);
|
||||
mantissa[startIndex] = '0';
|
||||
mantissa[startIndex+1] = '.';
|
||||
Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
|
||||
if (t > 0) {
|
||||
// copy only when significant digits are within the precision
|
||||
System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
|
||||
}
|
||||
} else if (t > 0) {
|
||||
mantissa = create(isNegative, zeros + 2 + t);
|
||||
mantissa[startIndex] = '0';
|
||||
mantissa[startIndex + 1] = '.';
|
||||
// copy only when significant digits are within the precision
|
||||
System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
|
||||
} else {
|
||||
this.mantissa = create(isNegative, 1);
|
||||
this.mantissa[startIndex] = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills mantissa and exponent char arrays for SCIENTIFIC format.
|
||||
*/
|
||||
private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
|
||||
int startIndex = isNegative ? 1 : 0;
|
||||
int t = Math.max(0, Math.min(nDigits - 1, precision));
|
||||
if (t > 0) {
|
||||
mantissa = create(isNegative, t + 2);
|
||||
mantissa[startIndex] = digits[0];
|
||||
mantissa[startIndex + 1] = '.';
|
||||
System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
|
||||
} else {
|
||||
mantissa = create(isNegative, 1);
|
||||
mantissa[startIndex] = digits[0];
|
||||
}
|
||||
char expSign;
|
||||
int e;
|
||||
if (exp <= 0) {
|
||||
expSign = '-';
|
||||
e = -exp + 1;
|
||||
} else {
|
||||
expSign = '+' ;
|
||||
e = exp - 1;
|
||||
}
|
||||
// decExponent has 1, 2, or 3, digits
|
||||
if (e <= 9) {
|
||||
exponent = new char[] { expSign,
|
||||
'0', (char) (e + '0') };
|
||||
} else if (e <= 99) {
|
||||
exponent = new char[] { expSign,
|
||||
(char) (e / 10 + '0'), (char) (e % 10 + '0') };
|
||||
} else {
|
||||
char hiExpChar = (char) (e / 100 + '0');
|
||||
e %= 100;
|
||||
exponent = new char[] { expSign,
|
||||
hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -931,6 +931,16 @@ public class Basic$Type$ extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -991,6 +1001,14 @@ public class Basic$Type$ extends Basic {
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
#if[BigDecimal]
|
||||
//---------------------------------------------------------------------
|
||||
// %f - BigDecimal
|
||||
@ -1187,6 +1205,16 @@ public class Basic$Type$ extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
#if[BigDecimal]
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -357,6 +357,16 @@ public class BasicBigDecimal extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -417,6 +427,14 @@ public class BasicBigDecimal extends Basic {
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
//---------------------------------------------------------------------
|
||||
// %f - BigDecimal
|
||||
//---------------------------------------------------------------------
|
||||
@ -591,6 +609,16 @@ public class BasicBigDecimal extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
//---------------------------------------------------------------------
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -329,6 +329,16 @@ public class BasicDouble extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -390,6 +400,14 @@ public class BasicDouble extends Basic {
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - float, double, Double, BigDecimal
|
||||
//---------------------------------------------------------------------
|
||||
@ -487,6 +505,16 @@ public class BasicDouble extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -328,6 +328,16 @@ public class BasicDoubleObject extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -389,6 +399,14 @@ public class BasicDoubleObject extends Basic {
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - float, double, Double, BigDecimal
|
||||
//---------------------------------------------------------------------
|
||||
@ -486,6 +504,16 @@ public class BasicDoubleObject extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -328,6 +328,16 @@ public class BasicFloat extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -389,6 +399,14 @@ public class BasicFloat extends Basic {
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - float
|
||||
//---------------------------------------------------------------------
|
||||
@ -489,6 +507,16 @@ public class BasicFloat extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -328,6 +328,16 @@ public class BasicFloatObject extends Basic {
|
||||
test("%3.0e", "1e+07", 10000000.00);
|
||||
test("%3.0e", "1e+08", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %e - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.1e", "9.9e-324", 1e-323);
|
||||
test("%.1e", "9.9e-323", 1e-322);
|
||||
test("%.15e", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.15e", "1.000000000000000e+23", 1e23);
|
||||
test("%.16e", "2.0000000000000000e+23", 2e23);
|
||||
test("%.16e", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f
|
||||
//
|
||||
@ -389,6 +399,14 @@ public class BasicFloatObject extends Basic {
|
||||
test("%,3.0f", "10,000,000", 10000000.00);
|
||||
test("%,3.0f", "100,000,000", 100000000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %f - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.0f", "738790000000000000000", 7.3879e20);
|
||||
test("%.0f", "100000000000000000000000", 1e23);
|
||||
test("%.0f", "200000000000000000000000", 2e23);
|
||||
test("%.0f", "19400994884341945000000000", 1.9400994884341945E25);
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
@ -474,6 +492,16 @@ public class BasicFloatObject extends Basic {
|
||||
|
||||
tryCatch("%#3.0g", FormatFlagsConversionMismatchException.class, 1000.00);
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// %g - adoption of Double.toString(double) algorithm
|
||||
//---------------------------------------------------------------------
|
||||
test("%.2g", "9.9e-324", 1e-323);
|
||||
test("%.2g", "9.9e-323", 1e-322);
|
||||
test("%.16g", "7.387900000000000e+20", 7.3879e20);
|
||||
test("%.16g", "1.000000000000000e+23", 1e23);
|
||||
test("%.17g", "2.0000000000000000e+23", 2e23);
|
||||
test("%.17g", "1.9400994884341945e+25", 1.9400994884341945E25);
|
||||
|
||||
// double PI^300
|
||||
// = 13962455701329742638131355433930076081862072808 ... e+149
|
||||
|
||||
|
@ -53,7 +53,7 @@ import org.junit.jupiter.params.provider.ValueSource;
|
||||
* @bug 4906370 4962433 4973103 4989961 5005818 5031150 4970931 4989491 5002937
|
||||
* 5005104 5007745 5061412 5055180 5066788 5088703 6317248 6318369 6320122
|
||||
* 6344623 6369500 6534606 6282094 6286592 6476425 5063507 6469160 6476168
|
||||
* 8059175 8204229
|
||||
* 8059175 8204229 8300869
|
||||
*
|
||||
* @run junit BasicTestLauncher
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user