diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 70a9b10f456..094c48bc02f 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -307,6 +307,20 @@ import java.util.Objects; * @since 1.1 */ public class BigDecimal extends Number implements Comparable { + /* + * 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 { * @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 { * @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); } /** diff --git a/test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java b/test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java new file mode 100644 index 00000000000..d02c1a5bc7b --- /dev/null +++ b/test/jdk/java/math/BigDecimal/DoubleFloatValueTests.java @@ -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(); + } + +}