8205592: BigDecimal.doubleValue() is depressingly slow
Reviewed-by: darcy
This commit is contained in:
parent
41ba05e450
commit
eb358619df
@ -307,6 +307,20 @@ import java.util.Objects;
|
||||
* @since 1.1
|
||||
*/
|
||||
public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
/*
|
||||
* Let l = log_2(10).
|
||||
* Then, L < l < L + ulp(L) / 2, that is, L = roundTiesToEven(l).
|
||||
*/
|
||||
private static final double L = 3.321928094887362;
|
||||
|
||||
private static final int P_F = Float.PRECISION; // 24
|
||||
private static final int Q_MIN_F = Float.MIN_EXPONENT - (P_F - 1); // -149
|
||||
private static final int Q_MAX_F = Float.MAX_EXPONENT - (P_F - 1); // 104
|
||||
|
||||
private static final int P_D = Double.PRECISION; // 53
|
||||
private static final int Q_MIN_D = (Double.MIN_EXPONENT - (P_D - 1)); // -1_074
|
||||
private static final int Q_MAX_D = (Double.MAX_EXPONENT - (P_D - 1)); // 971
|
||||
|
||||
/**
|
||||
* The unscaled value of this BigDecimal, as returned by {@link
|
||||
* #unscaledValue}.
|
||||
@ -3759,31 +3773,69 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
* @jls 5.1.3 Narrowing Primitive Conversion
|
||||
*/
|
||||
@Override
|
||||
public float floatValue(){
|
||||
if(intCompact != INFLATED) {
|
||||
public float floatValue() {
|
||||
/* For details, see the extensive comments in doubleValue(). */
|
||||
if (intCompact != INFLATED) {
|
||||
float v = intCompact;
|
||||
if (scale == 0) {
|
||||
return (float)intCompact;
|
||||
} else {
|
||||
/*
|
||||
* If both intCompact and the scale can be exactly
|
||||
* represented as float values, perform a single float
|
||||
* multiply or divide to compute the (properly
|
||||
* rounded) result.
|
||||
*/
|
||||
if (Math.abs(intCompact) < 1L<<22 ) {
|
||||
// Don't have too guard against
|
||||
// Math.abs(MIN_VALUE) because of outer check
|
||||
// against INFLATED.
|
||||
if (scale > 0 && scale < FLOAT_10_POW.length) {
|
||||
return (float)intCompact / FLOAT_10_POW[scale];
|
||||
} else if (scale < 0 && scale > -FLOAT_10_POW.length) {
|
||||
return (float)intCompact * FLOAT_10_POW[-scale];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
/*
|
||||
* The discussion for the double case also applies here. That is,
|
||||
* the following test is precise for all long values except for
|
||||
* Long.MAX_VALUE but the result is correct nevertheless.
|
||||
*/
|
||||
if ((long) v == intCompact) {
|
||||
if (0 < scale && scale < FLOAT_10_POW.length) {
|
||||
return v / FLOAT_10_POW[scale];
|
||||
}
|
||||
if (0 > scale && scale > -FLOAT_10_POW.length) {
|
||||
return v * FLOAT_10_POW[-scale];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Somewhat inefficient, but guaranteed to work.
|
||||
return Float.parseFloat(this.toString());
|
||||
return fullFloatValue();
|
||||
}
|
||||
|
||||
private float fullFloatValue() {
|
||||
if (intCompact == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
BigInteger w = unscaledValue().abs();
|
||||
long qb = w.bitLength() - (long) Math.ceil(scale * L);
|
||||
if (qb < Q_MIN_F - 2) { // qb < -151
|
||||
return signum() * 0.0f;
|
||||
}
|
||||
if (qb > Q_MAX_F + P_F + 1) { // qb > 129
|
||||
return signum() * Float.POSITIVE_INFINITY;
|
||||
}
|
||||
if (scale < 0) {
|
||||
return signum() * w.multiply(bigTenToThe(-scale)).floatValue();
|
||||
}
|
||||
if (scale == 0) {
|
||||
return signum() * w.floatValue();
|
||||
}
|
||||
int ql = (int) qb - (P_F + 3);
|
||||
BigInteger pow10 = bigTenToThe(scale);
|
||||
BigInteger m, n;
|
||||
if (ql <= 0) {
|
||||
m = w.shiftLeft(-ql);
|
||||
n = pow10;
|
||||
} else {
|
||||
m = w;
|
||||
n = pow10.shiftLeft(ql);
|
||||
}
|
||||
BigInteger[] qr = m.divideAndRemainder(n);
|
||||
int i = qr[0].intValue();
|
||||
int sb = qr[1].signum();
|
||||
int dq = (Integer.SIZE - (P_F + 2)) - Integer.numberOfLeadingZeros(i);
|
||||
int eq = (Q_MIN_F - 2) - ql;
|
||||
if (dq >= eq) {
|
||||
return signum() * Math.scalb((float) (i | sb), ql);
|
||||
}
|
||||
int mask = (1 << eq) - 1;
|
||||
int j = i >> eq | (Integer.signum(i & mask)) | sb;
|
||||
return signum() * Math.scalb((float) j, Q_MIN_F - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3804,31 +3856,257 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
* @jls 5.1.3 Narrowing Primitive Conversion
|
||||
*/
|
||||
@Override
|
||||
public double doubleValue(){
|
||||
if(intCompact != INFLATED) {
|
||||
public double doubleValue() {
|
||||
/*
|
||||
* Attempt a fast path when the significand is compact and the
|
||||
* scale is small enough.
|
||||
*/
|
||||
if (intCompact != INFLATED) {
|
||||
double v = intCompact;
|
||||
if (scale == 0) {
|
||||
return (double)intCompact;
|
||||
} else {
|
||||
/* v is the result of a single rounding. */
|
||||
return v;
|
||||
}
|
||||
/*
|
||||
* The test (long) (double) l == l to check whether l is an exact
|
||||
* double is always accurate, except for l = Long.MAX_VALUE.
|
||||
* This special case is not an issue, though, as explained below.
|
||||
*/
|
||||
if ((long) v == intCompact) {
|
||||
/*
|
||||
* If both intCompact and the scale can be exactly
|
||||
* represented as double values, perform a single
|
||||
* double multiply or divide to compute the (properly
|
||||
* rounded) result.
|
||||
* If intCompact != Long.MAX_VALUE, v is exactly equal to it
|
||||
* and 10^|scale| is an exact double when 0 < |scale| <= 22.
|
||||
* Hence, the multiplication or division below are on exact
|
||||
* doubles, so the result is subject to a single rounding.
|
||||
*
|
||||
* If intCompact = Long.MAX_VALUE, v is not exactly equal to it.
|
||||
* Luckily, when 0 < |scale| <= 22, full precision computations
|
||||
* show that the end result as computed here is correct anyway,
|
||||
* despite being the outcome of 2 roundings.
|
||||
*/
|
||||
if (Math.abs(intCompact) < 1L<<52 ) {
|
||||
// Don't have too guard against
|
||||
// Math.abs(MIN_VALUE) because of outer check
|
||||
// against INFLATED.
|
||||
if (scale > 0 && scale < DOUBLE_10_POW.length) {
|
||||
return (double)intCompact / DOUBLE_10_POW[scale];
|
||||
} else if (scale < 0 && scale > -DOUBLE_10_POW.length) {
|
||||
return (double)intCompact * DOUBLE_10_POW[-scale];
|
||||
}
|
||||
if (0 < scale && scale < DOUBLE_10_POW.length) {
|
||||
return v / DOUBLE_10_POW[scale];
|
||||
}
|
||||
if (0 > scale && scale > -DOUBLE_10_POW.length) {
|
||||
return v * DOUBLE_10_POW[-scale];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Somewhat inefficient, but guaranteed to work.
|
||||
return Double.parseDouble(this.toString());
|
||||
return fullDoubleValue();
|
||||
}
|
||||
|
||||
private double fullDoubleValue() {
|
||||
/*
|
||||
* This method works on all instances but might throw or consume a lot
|
||||
* of memory and cpu on huge scales or huge significands.
|
||||
*
|
||||
* It is expected that this computations might exhaust memory or consume
|
||||
* an unreasonable amount of cpu when both the significand and the scale
|
||||
* are huge and conjure to meet MIN < |this| < MAX, where MIN and MAX
|
||||
* are approximately Double.MIN_VALUE and Double.MAX_VALUE, resp.
|
||||
*/
|
||||
if (intCompact == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let
|
||||
* w = |unscaledValue()|
|
||||
* s = scale
|
||||
* bl = w.bitLength()
|
||||
* P = Double.PRECISION // 53
|
||||
* Q_MIN = Double.MIN_EXPONENT - (P - 1) // -1_074
|
||||
* Q_MAX = Double.MAX_EXPONENT - (P - 1) // 971
|
||||
* Thus
|
||||
* |this| = w 10^{-s}
|
||||
* Double.MIN_VALUE = 2^Q_MIN
|
||||
* Double.MAX_VALUE = (2^P - 1) 2^Q_MAX
|
||||
* Here w > 0, so 2^{bl-1} <= w < 2^bl, hence
|
||||
* bl = floor(log_2(w)) + 1
|
||||
*
|
||||
* To determine the return value, it helps to define real beta
|
||||
* and integer q meeting
|
||||
* w 10^{-s} = beta 2^q such that 2^{P+1} <= beta < 2^{P+2}
|
||||
* Note that floor(log_2(beta)) = P + 1.
|
||||
* The reason for having beta meet these inequalities rather than the
|
||||
* more "natural" 2^{P-1} <= beta < 2^P will become clearer below.
|
||||
* (They ensure that there's room for a "round" and a "sticky" bit.)
|
||||
*
|
||||
* Determining beta and q, however, requires costly computations.
|
||||
* Instead, try to quickly determine integer bounds ql, qh such that
|
||||
* ql <= q <= qh and with qh - ql as small as reasonably possible.
|
||||
* They help to quickly filter out most values that do not round
|
||||
* to a finite, non-zero double.
|
||||
*
|
||||
* To this end, let l = log_2(10). Then
|
||||
* log_2(w) - s l = log_2(w 10^{-s}) = log_2(beta) + q
|
||||
* Mathematically, for any real x, y:
|
||||
* floor(x) + floor(y) <= floor(x + y) <= floor(x) + floor(y) + 1
|
||||
* floor(-x) = -ceil(x)
|
||||
* Therefore, remembering that
|
||||
* floor(log_2(w)) = bl - 1 and floor(log_2(beta)) = P + 1
|
||||
* the above leads to
|
||||
* bl - ceil(s l) - P - 2 <= q <= bl - ceil(s l) - P - 1
|
||||
*
|
||||
* However, ceil(s l) is still a purely mathematical quantity.
|
||||
* To determine computable bounds for it, let L = roundTiesToEven(l)
|
||||
* and let u = 2^{-P} (see the comment about constant L).
|
||||
* Let * denote multiplication on doubles, which is subject to errors.
|
||||
* Then, since all involved values are not subnormals, it follows that
|
||||
* (see any textbook on numerical algorithms):
|
||||
* s * L = s l (1 + delta_1) (1 + delta_2) = s l (1 + theta)
|
||||
* where |delta_i| <= u, |theta| <= 2u / (1 - 2u) < 4u = 2^{2-P}
|
||||
* The delta_i account for the relative error of l and of *.
|
||||
* Note that s (the int scale) converts exactly as double.
|
||||
* Hence, as 3 < l < 4
|
||||
* |s * L - s l| = |s| l |theta| < 2^31 4 2^{2-P} = 2^{-18} < 1
|
||||
* For reals x, y, |x - y| <= 1 entails |ceil(x) - ceil(y)| <= 1. Thus,
|
||||
* ceil(s * L) - 1 <= ceil(s l) <= ceil(s * L) + 1
|
||||
*
|
||||
* Using these inequalities implies
|
||||
* bl - ceil(s * L) - P - 3 <= q <= bl - ceil(s * L) - P
|
||||
* finally leading to the definitions
|
||||
* qb = bl - ceil(s * L), ql = qb - P - 3, qh = qb - P
|
||||
* meeting
|
||||
* ql <= q <= qh and qh - ql = 3, which is small enough.
|
||||
* Note that qb doesn't always fit in an int.
|
||||
*
|
||||
* To filter out most values that round to 0 or infinity, define
|
||||
* ZCO = 1/2 2^Q_MIN = 2^{Q_MIN-1} (zero cutoff)
|
||||
* ICO = (2^P - 1/2) 2^Q_MAX (infinity cutoff)
|
||||
* Return [+/-]0 iff |this| <= ZCO, [+/-]infinity iff |this| >= ICO.
|
||||
*
|
||||
* To play safely, whenever 2^{P+2} 2^qh <= ZCO then
|
||||
* |this| = beta 2^q < 2^{P+2} 2^qh <= ZCO
|
||||
* Now, 2^{P+2} 2^qh <= ZCO means the same as P + 2 + qh < Q_MIN,
|
||||
* leading to
|
||||
* if qb < Q_MIN - 2 then return [+/-]0
|
||||
*
|
||||
* Similarly, whenever 2^{P+1} 2^ql >= 2^P 2^Q_MAX then
|
||||
* |this| = beta 2^q >= 2^{P+1} 2^ql >= 2^P 2^Q_MAX > ICO
|
||||
* Here, 2^{P+1} 2^ql >= 2^P 2^Q_MAX is equivalent to ql + 2 > Q_MAX,
|
||||
* which entails
|
||||
* if qb > Q_MAX + P + 1 then return [+/-]infinity
|
||||
*
|
||||
* Observe that |s * L| <= 2^31 4 = 2^33, so
|
||||
* (long) ceil(s * L) = ceil(s * L)
|
||||
* since all integers <= 2^P are exact doubles.
|
||||
*/
|
||||
BigInteger w = unscaledValue().abs();
|
||||
long qb = w.bitLength() - (long) Math.ceil(scale * L);
|
||||
if (qb < Q_MIN_D - 2) { // qb < -1_076
|
||||
return signum() * 0.0;
|
||||
}
|
||||
if (qb > Q_MAX_D + P_D + 1) { // qb > 1_025
|
||||
/* If s <= -309 then qb >= 1_027, so these cases all end up here. */
|
||||
return signum() * Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's still a small chance to return [+/-]0 or [+/-]infinity.
|
||||
* But rather than chasing for specific cases, do the full computations.
|
||||
* Here, Q_MIN - 2 <= qb <= Q_MAX + P + 1
|
||||
*/
|
||||
if (scale < 0) {
|
||||
/*
|
||||
* Here -309 < s < 0, so w 10^{-s} is an integer: delegate to
|
||||
* BigInteger.doubleValue() without further ado.
|
||||
* Also, |this| < 10^309, so the integers involved are manageable.
|
||||
*/
|
||||
return signum() * w.multiply(bigTenToThe(-scale)).doubleValue();
|
||||
}
|
||||
if (scale == 0) {
|
||||
return signum() * w.doubleValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* This last case has s > 0 and sometimes unmanageable large integers.
|
||||
* It is expected that these computations might exhaust memory or
|
||||
* consume an unreasonable amount of cpu when both w and s are huge.
|
||||
*
|
||||
* Assume a number eta >= 2^{P+1} and split it into i = floor(eta)
|
||||
* and f = eta - i. Thus i >= 2^{P+1} and 0 <= f < 1.
|
||||
* Define sb = 0 iff f = 0 and sb = 1 iff f > 0.
|
||||
* Let j = i | sb (| denotes bitwise "or").
|
||||
* j has at least P + 2 bits to accommodate P most significand bits
|
||||
* (msb), 1 rounding bit rb just to the right of them and 1 "sticky" bit
|
||||
* sb as its least significant bit, as depicted here:
|
||||
* eta = | P msb | rb | ... | lsb | bits of fraction f...
|
||||
* i = | P msb | rb | ... | lsb |
|
||||
* j = | P msb | rb | ... | sb |
|
||||
* All the bits in eta, i and j to the left of lsb or sb are identical.
|
||||
* It's not hard to see that
|
||||
* roundTiesToEven(eta) = roundTiesToEven(j)
|
||||
*
|
||||
* To apply the above, define
|
||||
* eta = (w/10^s) 2^{-ql}
|
||||
* which meets
|
||||
* eta = (w/10^s) 2^{-q} 2^{q-ql} = beta 2^{q-ql} = beta 2^dq
|
||||
* where dq = q - ql. Therefore, since ql <= q <= qh = ql + 3
|
||||
* 2^{P+1} <= eta < 2^{P+2} iff q = ql
|
||||
* 2^{P+2} <= eta < 2^{P+3} iff q = ql + 1
|
||||
* 2^{P+3} <= eta < 2^{P+4} iff q = ql + 2
|
||||
* 2^{P+4} <= eta < 2^{P+5} iff q = ql + 3
|
||||
* There are no other cases. The same holds for i = floor(eta),
|
||||
* which therefore fits in a long, as P + 5 < Long.SIZE:
|
||||
* 2^{P+1} <= i < 2^{P+2} iff q = ql
|
||||
* 2^{P+2} <= i < 2^{P+3} iff q = ql + 1
|
||||
* 2^{P+3} <= i < 2^{P+4} iff q = ql + 2
|
||||
* 2^{P+4} <= i < 2^{P+5} iff q = ql + 3
|
||||
* This shows dq = bitLength(i) - (P + 2).
|
||||
*
|
||||
* Let integer m = w 2^{-ql} if ql <= 0, or m = w if ql > 0 and
|
||||
* let integer n = 10^s if ql <= 0, or n = 10^s 2^ql if ql > 0.
|
||||
* It follows that eta = m/n, i = m // n, (// is integer division)
|
||||
* and f = (m \\ n) / n (\\ is binary "mod" (remainder)).
|
||||
* Of course, f > 0 iff m \\ n > 0, hence sb = signum(m \\ n).
|
||||
*
|
||||
* If q >= Q_MIN - 2 then |this| is in the normal range or overflows.
|
||||
* With eq = Q_MIN - 2 - ql the condition is the same as dq >= eq.
|
||||
* Provided |this| = eta 2^ql does not overflow, it follows that
|
||||
* roundTiesToEven(|this|) = roundTiesToEven(eta) 2^ql
|
||||
* = roundTiesToEven(j) 2^ql = scalb((double) j, ql)
|
||||
* If |this| overflows, however, so does scalb((double) j, ql). Thus,
|
||||
* in either case
|
||||
* roundTiesToEven(|this|) = scalb((double) j, ql)
|
||||
*
|
||||
* When q < Q_MIN - 2, that is, when dq < eq, |this| is in the
|
||||
* subnormal range. The integer j needs to be shortened to ensure that
|
||||
* the precision is gradually shortened for the final significand.
|
||||
* |this| = eta 2^ql = (eta/2^eq) 2^{Q_MIN-2}
|
||||
* Compare eta and i as depicted here
|
||||
* eta = | msb | eq lsb | bits of fraction f...
|
||||
* i = | msb | eq lsb |
|
||||
* where there are eq least significant bits in the right section.
|
||||
* To obtain j in this case, shift i to the right by eq positions and
|
||||
* thereafter "or" its least significant bit with signum(eq lsb) and
|
||||
* with sb as defined above. This leads to
|
||||
* roundTiesToEven(|this|) = scalb((double) j, Q_MIN - 2)
|
||||
*/
|
||||
int ql = (int) qb - (P_D + 3); // narrowing qb to an int is safe
|
||||
BigInteger pow10 = bigTenToThe(scale);
|
||||
BigInteger m, n;
|
||||
if (ql <= 0) {
|
||||
m = w.shiftLeft(-ql);
|
||||
n = pow10;
|
||||
} else {
|
||||
m = w;
|
||||
n = pow10.shiftLeft(ql);
|
||||
}
|
||||
|
||||
BigInteger[] qr = m.divideAndRemainder(n);
|
||||
long i = qr[0].longValue();
|
||||
int sb = qr[1].signum();
|
||||
int dq = (Long.SIZE - (P_D + 2)) - Long.numberOfLeadingZeros(i);
|
||||
int eq = (Q_MIN_D - 2) - ql;
|
||||
if (dq >= eq) {
|
||||
return signum() * Math.scalb((double) (i | sb), ql);
|
||||
}
|
||||
|
||||
/* Subnormal */
|
||||
long mask = (1L << eq) - 1;
|
||||
long j = i >> eq | Long.signum(i & mask) | sb;
|
||||
return signum() * Math.scalb((double) j, Q_MIN_D - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
259
test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java
Normal file
259
test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8205592
|
||||
* @summary Verify {double, float}Value methods work
|
||||
* @library /test/lib
|
||||
* @key randomness
|
||||
* @build jdk.test.lib.RandomFactory
|
||||
* @run main DoubleFloatValueTests
|
||||
*/
|
||||
|
||||
import jdk.test.lib.RandomFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Random;
|
||||
|
||||
public class DoubleFloatValueTests {
|
||||
private static final BigDecimal HALF = BigDecimal.valueOf(5, 1);
|
||||
private static final BigDecimal EPS = BigDecimal.valueOf(1, 10_000);
|
||||
|
||||
private static BigDecimal nextHalfUp(double v) {
|
||||
BigDecimal bv = new BigDecimal(v);
|
||||
BigDecimal ulp = new BigDecimal(Math.ulp(v));
|
||||
return bv.add(ulp.multiply(HALF));
|
||||
}
|
||||
|
||||
private static BigDecimal nextHalfDown(double v) {
|
||||
BigDecimal bv = new BigDecimal(v);
|
||||
BigDecimal ulp = new BigDecimal(v - Math.nextDown(v));
|
||||
return bv.subtract(ulp.multiply(HALF));
|
||||
}
|
||||
|
||||
private static BigDecimal nextHalfUp(float v) {
|
||||
BigDecimal bv = new BigDecimal(v);
|
||||
BigDecimal ulp = new BigDecimal(Math.ulp(v));
|
||||
return bv.add(ulp.multiply(HALF));
|
||||
}
|
||||
|
||||
private static BigDecimal nextHalfDown(float v) {
|
||||
BigDecimal bv = new BigDecimal(v);
|
||||
BigDecimal ulp = new BigDecimal(v - Math.nextDown(v));
|
||||
return bv.subtract(ulp.multiply(HALF));
|
||||
}
|
||||
|
||||
private static String toDecHexString(double v) {
|
||||
return v + " (" + Double.toHexString(v) + ")";
|
||||
}
|
||||
|
||||
private static String toDecHexString(float v) {
|
||||
return v + " (" + Float.toHexString(v) + ")";
|
||||
}
|
||||
|
||||
private static void checkDouble(BigDecimal bd, double exp) {
|
||||
double res = bd.doubleValue();
|
||||
if (exp != res ) {
|
||||
String message = "Bad conversion: got " + toDecHexString(res) +
|
||||
", expected " + toDecHexString(exp);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkFloat(BigDecimal bv, float exp) {
|
||||
float res = bv.floatValue();
|
||||
if (exp != res ) {
|
||||
String message = "Bad conversion: got " + toDecHexString(res) +
|
||||
", expected " + toDecHexString(exp);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOdd(int n) {
|
||||
return (n & 0x1) != 0;
|
||||
}
|
||||
|
||||
private static void testDoubleValueNearMinValue() {
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal b = nextHalfUp(n * Double.MIN_VALUE);
|
||||
checkDouble(b, ((n + 1) / 2 * 2) * Double.MIN_VALUE);
|
||||
checkDouble(b.subtract(EPS), n * Double.MIN_VALUE);
|
||||
checkDouble(b.add(EPS), (n + 1) * Double.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testFloatValueNearMinValue() {
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal b = nextHalfUp(n * Float.MIN_VALUE);
|
||||
checkFloat(b, ((n + 1) / 2 * 2) * Float.MIN_VALUE);
|
||||
checkFloat(b.subtract(EPS), n * Float.MIN_VALUE);
|
||||
checkFloat(b.add(EPS), (n + 1) * Float.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testDoubleValueNearMinNormal() {
|
||||
double v = Double.MIN_NORMAL;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfDown(v);
|
||||
checkDouble(bv, isOdd(n) ? Math.nextDown(v) : v);
|
||||
checkDouble(bv.subtract(EPS), Math.nextDown(v));
|
||||
checkDouble(bv.add(EPS), v);
|
||||
v = Math.nextDown(v);
|
||||
}
|
||||
v = Double.MIN_NORMAL;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfUp(v);
|
||||
checkDouble(bv, isOdd(n) ? Math.nextUp(v) : v);
|
||||
checkDouble(bv.subtract(EPS), v);
|
||||
checkDouble(bv.add(EPS), Math.nextUp(v));
|
||||
v = Math.nextUp(v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testFloatValueNearMinNormal() {
|
||||
float v = Float.MIN_NORMAL;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfDown(v);
|
||||
checkFloat(bv, isOdd(n) ? Math.nextDown(v) : v);
|
||||
checkFloat(bv.subtract(EPS), Math.nextDown(v));
|
||||
checkFloat(bv.add(EPS), v);
|
||||
v = Math.nextDown(v);
|
||||
}
|
||||
v = Float.MIN_NORMAL;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfUp(v);
|
||||
checkFloat(bv, isOdd(n) ? Math.nextUp(v) : v);
|
||||
checkFloat(bv.subtract(EPS), v);
|
||||
checkFloat(bv.add(EPS), Math.nextUp(v));
|
||||
v = Math.nextUp(v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testDoubleValueNearMaxValue() {
|
||||
double v = Double.MAX_VALUE;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfDown(v);
|
||||
checkDouble(bv, isOdd(n) ? v : Math.nextDown(v));
|
||||
checkDouble(bv.subtract(EPS), Math.nextDown(v));
|
||||
checkDouble(bv.add(EPS), v);
|
||||
v = Math.nextDown(v);
|
||||
}
|
||||
BigDecimal bv = nextHalfUp(Double.MAX_VALUE);
|
||||
checkDouble(bv, Double.POSITIVE_INFINITY);
|
||||
checkDouble(bv.subtract(EPS), Double.MAX_VALUE);
|
||||
checkDouble(bv.add(EPS), Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
private static void testFloatValueNearMaxValue() {
|
||||
float v = Float.MAX_VALUE;
|
||||
for (int n = 0; n < 100; ++n) {
|
||||
BigDecimal bv = nextHalfDown(v);
|
||||
checkFloat(bv, isOdd(n) ? v : Math.nextDown(v));
|
||||
checkFloat(bv.subtract(EPS), Math.nextDown(v));
|
||||
checkFloat(bv.add(EPS), v);
|
||||
v = Math.nextDown(v);
|
||||
}
|
||||
BigDecimal bv = nextHalfUp(Float.MAX_VALUE);
|
||||
checkFloat(bv, Float.POSITIVE_INFINITY);
|
||||
checkFloat(bv.subtract(EPS), Float.MAX_VALUE);
|
||||
checkFloat(bv.add(EPS), Float.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
private static void testDoubleValueRandom() {
|
||||
Random r = RandomFactory.getRandom();
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
double v = r.nextDouble(-Double.MAX_VALUE, Double.MAX_VALUE);
|
||||
checkDouble(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
double v = r.nextDouble(-1e9, 1e9);
|
||||
checkDouble(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
double v = r.nextDouble(-1e6, 1e6);
|
||||
checkDouble(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
double v = r.nextDouble(-1e-6, 1e-6);
|
||||
checkDouble(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
double v = r.nextDouble(-1e-9, 1e-9);
|
||||
checkDouble(new BigDecimal(v), v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testFloatValueRandom() {
|
||||
Random r = RandomFactory.getRandom();
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
float v = r.nextFloat(-Float.MAX_VALUE, Float.MAX_VALUE);
|
||||
checkFloat(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
float v = r.nextFloat(-1e9f, 1e9f);
|
||||
checkFloat(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
float v = r.nextFloat(-1e6f, 1e6f);
|
||||
checkFloat(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
float v = r.nextFloat(-1e-6f, 1e-6f);
|
||||
checkFloat(new BigDecimal(v), v);
|
||||
}
|
||||
for (int i = 0; i < 10_000; ++i) {
|
||||
float v = r.nextFloat(-1e-9f, 1e-9f);
|
||||
checkFloat(new BigDecimal(v), v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testDoubleValueExtremes() {
|
||||
checkDouble(BigDecimal.valueOf(1, 1000), 0.0);
|
||||
checkDouble(BigDecimal.valueOf(-1, 1000), -0.0);
|
||||
checkDouble(BigDecimal.valueOf(1, -1000), Double.POSITIVE_INFINITY);
|
||||
checkDouble(BigDecimal.valueOf(-1, -1000), -Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
private static void testFloatValueExtremes() {
|
||||
checkFloat(BigDecimal.valueOf(1, 1000), 0.0f);
|
||||
checkFloat(BigDecimal.valueOf(-1, 1000), -0.0f);
|
||||
checkFloat(BigDecimal.valueOf(1, -1000), Float.POSITIVE_INFINITY);
|
||||
checkFloat(BigDecimal.valueOf(-1, -1000), -Float.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
testDoubleValueNearMinValue();
|
||||
testDoubleValueNearMinNormal();
|
||||
testDoubleValueNearMaxValue();
|
||||
testDoubleValueRandom();
|
||||
testDoubleValueExtremes();
|
||||
|
||||
testFloatValueNearMinValue();
|
||||
testFloatValueNearMinNormal();
|
||||
testFloatValueNearMaxValue();
|
||||
testFloatValueRandom();
|
||||
testFloatValueExtremes();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user