8205592: BigDecimal.doubleValue() is depressingly slow

Reviewed-by: darcy
This commit is contained in:
Raffaello Giulietti 2023-04-27 17:01:56 +00:00
parent 41ba05e450
commit eb358619df
2 changed files with 577 additions and 40 deletions

View File

@ -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);
}
/**

View 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();
}
}