mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8366017: Extend the set of inputs handled by fast paths in FloatingDecimal
Reviewed-by: darcy
This commit is contained in:
parent
400f51f7ab
commit
deb7edb151
@ -322,7 +322,7 @@ final class DigitList implements Cloneable {
|
||||
void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
|
||||
assert Double.isFinite(source);
|
||||
|
||||
FloatingDecimal.BinaryToASCIIConverter fdConverter =
|
||||
FloatingDecimal.BinaryToASCIIBuffer fdConverter =
|
||||
FloatingDecimal.getBinaryToASCIIConverter(source, COMPAT);
|
||||
boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp();
|
||||
boolean valueExactAsDecimal = fdConverter.decimalDigitsExact();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -43,7 +43,7 @@ final class MathUtils {
|
||||
*/
|
||||
|
||||
/*
|
||||
* N = max{n : 10^n - 1 <= 2^Long.SIZE - 1}
|
||||
* N = max{n : 10^n - 1 ≤ 2^Long.SIZE - 1}
|
||||
* Every positive integer up to N decimal digits fits in an unsigned long,
|
||||
* but there are positive integers of N + 1 digits that do not.
|
||||
*/
|
||||
@ -52,16 +52,16 @@ final class MathUtils {
|
||||
/*
|
||||
* Given integer e, let 10^e = beta 2^r for the unique integer r and
|
||||
* real beta meeting
|
||||
* 2^125 <= beta < 2^126.
|
||||
* 2^125 ≤ beta < 2^126.
|
||||
* The unique r and beta are
|
||||
* r = floor(log2(10^e)) - 125, beta = 10^e 2^(-r)
|
||||
* Let g = floor(beta) + 1.
|
||||
* Thus,
|
||||
* (g - 1) 2^r <= 10^e < g 2^r
|
||||
* (g - 1) 2^r ≤ 10^e < g 2^r
|
||||
* Further, let
|
||||
* g1 = floor(g 2^(-63)), g0 = g - g1 2^63
|
||||
*
|
||||
* For e with GE_MIN <= e <= GE_MAX, the values g1 and g0 are available
|
||||
* For e with GE_MIN ≤ e ≤ GE_MAX, the values g1 and g0 are available
|
||||
* by invoking methods g1(e) and g0(e).
|
||||
* In addition, r = flog2pow10(e) - 125.
|
||||
*
|
||||
@ -73,17 +73,27 @@ final class MathUtils {
|
||||
* discussed in [1].
|
||||
* (See DoubleToDecimal.K_MIN and DoubleToDecimal.K_MAX.)
|
||||
*
|
||||
* Let x = f 10^ep, where integers f and ep meet 10^(n-1) <= f < 10^n
|
||||
* Let x = f 10^ep, where integers f and ep meet 10^(n-1) ≤ f < 10^n
|
||||
* (that is, f has n digits) and E_THR_Z < ep + n < E_THR_I.
|
||||
* (See DoubleToDecimal.E_THR_Z and DoubleToDecimal.E_THR_I.)
|
||||
*
|
||||
* The decimal->double fast paths assume n <= N.
|
||||
* Thus, E_THR_Z - (N - 1) <= ep <= E_THR_I - 2, which means that
|
||||
* The decimal->double fast paths assume n ≤ N.
|
||||
* Thus, E_THR_Z - (N - 1) ≤ ep ≤ E_THR_I - 2, which means that
|
||||
* the interval [GE_MIN, GE_MAX] must include the interval
|
||||
* [E_THR_Z - (N - 1), E_THR_I - 2].
|
||||
* That is,
|
||||
* GE_MIN = min(-K_MAX, E_THR_Z - (N - 1))
|
||||
* GE_MAX = max(-K_MIN, E_THR_I - 2)
|
||||
*
|
||||
* For the record, while the definition of g allows the case g = 2^126,
|
||||
* the maximal g1 in the lookup table is 0x7FDD_E7F4_CA72_E30FL (e = -146),
|
||||
* the minimal g1 is 0x4000_0000_0000_0000L (for e = 0),
|
||||
* and the minimal g0 is 1.
|
||||
* Hence, as easily verified, we always have
|
||||
* 2^62 < g1 + 1 < 2^31 (2^32 - 1) < 2^63
|
||||
* 2^125 < g = g1 2^63 + g0 < (g1 + 1) 2^63 < 2^94 (2^32 - 1) < 2^126
|
||||
* We will assume these bounds observed by glancing at the lookup table,
|
||||
* and use them liberally when so needed.
|
||||
*/
|
||||
static final int GE_MIN = -342;
|
||||
static final int GE_MAX = 324;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2025, 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
|
||||
@ -23,7 +23,8 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568 7039369 4396272
|
||||
* @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568
|
||||
* 7039369 4396272 8366017
|
||||
* @summary Test for Double.parseDouble method and acceptance regex
|
||||
*/
|
||||
|
||||
@ -759,6 +760,39 @@ public class ParseDouble {
|
||||
throw new RuntimeException("Inconsistent conversion");
|
||||
}
|
||||
|
||||
private static void testFastPaths() {
|
||||
/* Exercises the fast paths in jdk.internal.math.FloatingDecimal. */
|
||||
check("1", 0x1.0p0); // 1.0
|
||||
check("2.34000e2", 0x1.d4p7); // 234.0
|
||||
check("9.223e18", 0x1.fffab689adb6p62); // 9.223e18
|
||||
check("9.876e18", 0x1.121d33597384p63); // 9.876e18
|
||||
check("9223372036854776833", 0x1.0000000000001p63); // 9.223372036854778E18
|
||||
check("9223372036854776832", 0x1.0p63); // 9.223372036854776E18
|
||||
|
||||
check("1.23", 0x1.3ae147ae147aep0); // 1.23
|
||||
check("0.000234", 0x1.eabbcb1cc9646p-13); // 2.34E-4
|
||||
check("3.45e23", 0x1.2439f32cbea41p78); // 3.45E23
|
||||
check("576460752303423616e20", 0x1.5af1d78b58c41p125); // 5.764607523034236E37
|
||||
|
||||
check("1e37", 0x1.e17b84357691bp122); // 1.0E37
|
||||
check("8999e34", 0x1.0ecdc63717fbdp126); // 8.999E37
|
||||
check("0.9999e36", 0x1.8125c09cb78b7p119); // 9.999E35
|
||||
check("0.9876e37", 0x1.db831933296cep122); // 9.876E36
|
||||
|
||||
check("1.2e-200", 0x1.d64af4cc52935p-665); // 1.2E-200
|
||||
check("2.3e100", 0x1.507ed84d17a69p333); // 2.3E100
|
||||
check("1.2000000000000000003e-200", 0x1.d64af4cc52935p-665); // 1.2E-200
|
||||
check("2.3000000000000000004e100", 0x1.507ed84d17a69p333); // 2.3E100
|
||||
check("5.249320425370670463e308", Double.POSITIVE_INFINITY);
|
||||
check("5.2493204253706704633e308", Double.POSITIVE_INFINITY);
|
||||
|
||||
check("1.2e-320", 0x0.000000000097dp-1022); // 1.2E-320
|
||||
check("1.2000000000000000003e-320", 0x0.000000000097dp-1022); // 1.2E-320
|
||||
|
||||
check("2.225073858507201383e-308", Double.MIN_NORMAL);
|
||||
check("2.2250738585072013831e-308", Double.MIN_NORMAL);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
rudimentaryTest();
|
||||
|
||||
@ -775,5 +809,8 @@ public class ParseDouble {
|
||||
testSubnormalPowers();
|
||||
testPowers();
|
||||
testStrictness();
|
||||
|
||||
testFastPaths();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2025, 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
|
||||
@ -23,8 +23,10 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4160406 4705734 4707389 6358355 7032154
|
||||
* @summary Tests for Float.parseFloat method
|
||||
* @bug 4160406 4705734 4707389 6358355 7032154 8366017
|
||||
* @summary Tests for Float.parseFloat method (use -DallRoundtrips=true
|
||||
* to additionally check all non-negative, non-NaN float->String->float
|
||||
* roundtrips)
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@ -315,6 +317,22 @@ public class ParseFloat {
|
||||
check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString());
|
||||
}
|
||||
|
||||
private static final int N = Float.floatToIntBits(Float.POSITIVE_INFINITY);
|
||||
|
||||
/* Tests all non-negative, non-NaN float->String->float roundtrips. */
|
||||
private static void testAllRoundtrips() {
|
||||
for (int i = 0; i <= N; ++i) {
|
||||
float v = Float.intBitsToFloat(i);
|
||||
String s = Float.toString(v);
|
||||
float v0 = Float.parseFloat(s);
|
||||
if (v != v0) {
|
||||
fail(s, v0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String ALL_ROUNDTRIPS_PROP = "allRoundtrips";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
rudimentaryTest();
|
||||
|
||||
@ -324,5 +342,9 @@ public class ParseFloat {
|
||||
testParsing(paddedBadStrings, true);
|
||||
|
||||
testPowers();
|
||||
|
||||
if (Boolean.getBoolean(ALL_ROUNDTRIPS_PROP)) {
|
||||
testAllRoundtrips();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2025, 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
|
||||
@ -21,415 +21,21 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import jdk.internal.math.FDBigInteger;
|
||||
import jdk.internal.math.FDBigIntegerChecker;
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 7032154 8342693
|
||||
* @summary unit tests of FDBigInteger
|
||||
* @modules java.base/jdk.internal.math
|
||||
* @author Dmitry Nadezhin
|
||||
* @library java.base
|
||||
* @build java.base/jdk.internal.math.*
|
||||
* @run main TestFDBigInteger
|
||||
*/
|
||||
public class TestFDBigInteger {
|
||||
|
||||
private static final int MAX_P5 = 413;
|
||||
private static final int MAX_P2 = 65;
|
||||
private static final long LONG_SIGN_MASK = (1L << 63);
|
||||
private static final BigInteger FIVE = BigInteger.valueOf(5);
|
||||
private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
|
||||
private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
|
||||
private static final FDBigInteger IMMUTABLE_MILLION = genMillion1();
|
||||
private static final FDBigInteger IMMUTABLE_BILLION = genBillion1();
|
||||
private static final FDBigInteger IMMUTABLE_TEN18 = genTen18();
|
||||
|
||||
static {
|
||||
IMMUTABLE_ZERO.makeImmutable();
|
||||
IMMUTABLE_MILLION.makeImmutable();
|
||||
IMMUTABLE_BILLION.makeImmutable();
|
||||
IMMUTABLE_TEN18.makeImmutable();
|
||||
}
|
||||
|
||||
private static FDBigInteger mutable(String hex, int offset) {
|
||||
byte[] chars = new BigInteger(hex, 16).toString().getBytes(StandardCharsets.US_ASCII);
|
||||
return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
|
||||
}
|
||||
|
||||
private static FDBigInteger immutable(String hex, int offset) {
|
||||
FDBigInteger fd = mutable(hex, offset);
|
||||
fd.makeImmutable();
|
||||
return fd;
|
||||
}
|
||||
|
||||
private static BigInteger biPow52(int p5, int p2) {
|
||||
return FIVE.pow(p5).shiftLeft(p2);
|
||||
}
|
||||
|
||||
// data.length == 1, nWords == 1, offset == 0
|
||||
private static FDBigInteger genMillion1() {
|
||||
return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
|
||||
}
|
||||
|
||||
// data.length == 2, nWords == 1, offset == 0
|
||||
private static FDBigInteger genMillion2() {
|
||||
return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
|
||||
}
|
||||
|
||||
// data.length == 1, nWords == 1, offset == 0
|
||||
private static FDBigInteger genBillion1() {
|
||||
return FDBigInteger.valueOfPow52(9, 0).leftShift(9);
|
||||
}
|
||||
|
||||
// data.length == 2, nWords == 2, offset == 0
|
||||
private static FDBigInteger genTen18() {
|
||||
return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
|
||||
}
|
||||
|
||||
private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
|
||||
if (!expected.equals(actual.toBigInteger())) {
|
||||
throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16));
|
||||
}
|
||||
}
|
||||
|
||||
private static void testValueOfPow52(int p5, int p2) throws Exception {
|
||||
check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
|
||||
"valueOfPow52(" + p5 + "," + p2 + ")");
|
||||
}
|
||||
|
||||
private static void testValueOfPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
testValueOfPow52(p5, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
|
||||
BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
|
||||
if (value < 0) {
|
||||
bi = bi.setBit(63);
|
||||
}
|
||||
check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
|
||||
"valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52(long value, int p5) throws Exception {
|
||||
testValueOfMulPow52(value, p5, 0);
|
||||
testValueOfMulPow52(value, p5, 1);
|
||||
testValueOfMulPow52(value, p5, 30);
|
||||
testValueOfMulPow52(value, p5, 31);
|
||||
testValueOfMulPow52(value, p5, 33);
|
||||
testValueOfMulPow52(value, p5, 63);
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
testValueOfMulPow52(0xFFFFFFFFL, p5);
|
||||
testValueOfMulPow52(0x123456789AL, p5);
|
||||
testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
|
||||
testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
FDBigInteger r = t.leftShift(shift);
|
||||
if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
|
||||
throw new Exception("leftShift doesn't reuse its argument");
|
||||
}
|
||||
if (isImmutable) {
|
||||
check(bt, t, "leftShift corrupts its argument");
|
||||
}
|
||||
check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
|
||||
}
|
||||
|
||||
private static void testLeftShift() throws Exception {
|
||||
testLeftShift(IMMUTABLE_ZERO, 0, true);
|
||||
testLeftShift(IMMUTABLE_ZERO, 10, true);
|
||||
testLeftShift(MUTABLE_ZERO, 0, false);
|
||||
testLeftShift(MUTABLE_ZERO, 10, false);
|
||||
|
||||
testLeftShift(IMMUTABLE_MILLION, 0, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 1, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 12, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 13, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 32, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 33, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 44, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 45, true);
|
||||
|
||||
testLeftShift(genMillion1(), 0, false);
|
||||
testLeftShift(genMillion1(), 1, false);
|
||||
testLeftShift(genMillion1(), 12, false);
|
||||
testLeftShift(genMillion1(), 13, false);
|
||||
testLeftShift(genMillion1(), 25, false);
|
||||
testLeftShift(genMillion1(), 26, false);
|
||||
testLeftShift(genMillion1(), 32, false);
|
||||
testLeftShift(genMillion1(), 33, false);
|
||||
testLeftShift(genMillion1(), 44, false);
|
||||
testLeftShift(genMillion1(), 45, false);
|
||||
|
||||
testLeftShift(genMillion2(), 0, false);
|
||||
testLeftShift(genMillion2(), 1, false);
|
||||
testLeftShift(genMillion2(), 12, false);
|
||||
testLeftShift(genMillion2(), 13, false);
|
||||
testLeftShift(genMillion2(), 25, false);
|
||||
testLeftShift(genMillion2(), 26, false);
|
||||
testLeftShift(genMillion2(), 32, false);
|
||||
testLeftShift(genMillion2(), 33, false);
|
||||
testLeftShift(genMillion2(), 44, false);
|
||||
testLeftShift(genMillion2(), 45, false);
|
||||
}
|
||||
|
||||
private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
BigInteger bs = s.toBigInteger();
|
||||
int q = t.quoRemIteration(s);
|
||||
BigInteger[] qr = bt.divideAndRemainder(bs);
|
||||
if (!BigInteger.valueOf(q).equals(qr[0])) {
|
||||
throw new Exception("quoRemIteration returns incorrect quo");
|
||||
}
|
||||
check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
|
||||
}
|
||||
|
||||
private static void testQuoRemIteration() throws Exception {
|
||||
// IMMUTABLE_TEN18 == 0de0b6b3a7640000
|
||||
// q = 0
|
||||
testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
|
||||
// q = 1 -> q = 0
|
||||
testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
|
||||
// q = 1
|
||||
testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
|
||||
// q = 18
|
||||
testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
|
||||
}
|
||||
|
||||
private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
BigInteger bo = o.toBigInteger();
|
||||
int cmp = t.cmp(o);
|
||||
int bcmp = bt.compareTo(bo);
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("cmp returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "cmp corrupts this");
|
||||
check(bo, o, "cmp corrupts other");
|
||||
if (o.cmp(t) != -cmp) {
|
||||
throw new Exception("asymmetrical cmp");
|
||||
}
|
||||
check(bt, t, "cmp corrupts this");
|
||||
check(bo, o, "cmp corrupts other");
|
||||
}
|
||||
|
||||
private static void testCmp() throws Exception {
|
||||
testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
|
||||
testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
|
||||
testCmp(mutable("5", 0), mutable("6", 0));
|
||||
testCmp(mutable("5", 0), mutable("5", 0));
|
||||
testCmp(mutable("5000000001", 0), mutable("500000001", 0));
|
||||
testCmp(mutable("5000000001", 0), mutable("6", 1));
|
||||
testCmp(mutable("5000000001", 0), mutable("5", 1));
|
||||
testCmp(mutable("5000000000", 0), mutable("5", 1));
|
||||
}
|
||||
|
||||
private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
|
||||
FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
|
||||
BigInteger bt = t.toBigInteger();
|
||||
BigInteger bo = biPow52(p5, p2);
|
||||
int cmp = t.cmp(o);
|
||||
int bcmp = bt.compareTo(bo);
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "cmpPow52 corrupts this");
|
||||
check(bo, o, "cmpPow5 corrupts other");
|
||||
}
|
||||
|
||||
private static void testCmpPow52() throws Exception {
|
||||
testCmpPow52(mutable("00000002", 1), 0, 31);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 32);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 33);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 34);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 64);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 32);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 33);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 34);
|
||||
}
|
||||
|
||||
private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
BigInteger bx = x.toBigInteger();
|
||||
BigInteger by = y.toBigInteger();
|
||||
int cmp = t.addAndCmp(x, y);
|
||||
int bcmp = bt.compareTo(bx.add(by));
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "addAndCmp corrupts this");
|
||||
check(bx, x, "addAndCmp corrupts x");
|
||||
check(by, y, "addAndCmp corrupts y");
|
||||
}
|
||||
|
||||
private static void testAddAndCmp() throws Exception {
|
||||
testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
|
||||
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
|
||||
|
||||
testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
|
||||
testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
}
|
||||
|
||||
private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
FDBigInteger r = t.multBy10();
|
||||
if ((bt.signum() == 0 || !isImmutable) && r != t) {
|
||||
throw new Exception("multBy10 of doesn't reuse its argument");
|
||||
}
|
||||
if (isImmutable) {
|
||||
check(bt, t, "multBy10 corrupts its argument");
|
||||
}
|
||||
check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
|
||||
}
|
||||
|
||||
private static void testMultBy10() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
testMultBy10(value, false);
|
||||
value.makeImmutable();
|
||||
testMultBy10(value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
|
||||
BigInteger bt = t.toBigInteger();
|
||||
FDBigInteger r = t.multByPow52(p5, p2);
|
||||
if (bt.signum() == 0 && r != t) {
|
||||
throw new Exception("multByPow52 of doesn't reuse its argument");
|
||||
}
|
||||
check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
|
||||
}
|
||||
|
||||
private static void testMultByPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
testMultByPow52(value, p5, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
|
||||
BigInteger biLeft = left.toBigInteger();
|
||||
BigInteger biRight = right.toBigInteger();
|
||||
FDBigInteger diff = left.leftInplaceSub(right);
|
||||
if (!isImmutable && diff != left) {
|
||||
throw new Exception("leftInplaceSub of doesn't reuse its argument");
|
||||
}
|
||||
if (isImmutable) {
|
||||
check(biLeft, left, "leftInplaceSub corrupts its left immutable argument");
|
||||
}
|
||||
check(biRight, right, "leftInplaceSub corrupts its right argument");
|
||||
check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result");
|
||||
}
|
||||
|
||||
private static void testLeftInplaceSub() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// for (int p5r = 0; p5r <= p5; p5r += 10) {
|
||||
// for (int p2r = 0; p2r <= p2; p2r += 10) {
|
||||
for (int p5r = 0; p5r <= p5; p5r++) {
|
||||
for (int p2r = 0; p2r <= p2; p2r++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
|
||||
testLeftInplaceSub(left, right, false);
|
||||
left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
left.makeImmutable();
|
||||
testLeftInplaceSub(left, right, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
|
||||
BigInteger biLeft = left.toBigInteger();
|
||||
BigInteger biRight = right.toBigInteger();
|
||||
FDBigInteger diff = left.rightInplaceSub(right);
|
||||
if (!isImmutable && diff != right) {
|
||||
throw new Exception("rightInplaceSub of doesn't reuse its argument");
|
||||
}
|
||||
check(biLeft, left, "leftInplaceSub corrupts its left argument");
|
||||
if (isImmutable) {
|
||||
check(biRight, right, "leftInplaceSub corrupts its right immutable argument");
|
||||
}
|
||||
try {
|
||||
check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result");
|
||||
} catch (Exception e) {
|
||||
System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void testRightInplaceSub() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// for (int p5r = 0; p5r <= p5; p5r += 10) {
|
||||
// for (int p2r = 0; p2r <= p2; p2r += 10) {
|
||||
for (int p5r = 0; p5r <= p5; p5r++) {
|
||||
for (int p2r = 0; p2r <= p2; p2r++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
|
||||
testRightInplaceSub(left, right, false);
|
||||
right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
|
||||
right.makeImmutable();
|
||||
testRightInplaceSub(left, right, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testValueOfPow52();
|
||||
testValueOfMulPow52();
|
||||
testLeftShift();
|
||||
testQuoRemIteration();
|
||||
testCmp();
|
||||
testCmpPow52();
|
||||
testAddAndCmp();
|
||||
// Uncomment the following for more comprehensize but slow testing.
|
||||
// testLeftInplaceSub();
|
||||
// testMultBy10();
|
||||
// testMultByPow52();
|
||||
// testRightInplaceSub();
|
||||
FDBigIntegerChecker.main(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2025, 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.
|
||||
*/
|
||||
|
||||
package jdk.internal.math;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class FDBigIntegerChecker {
|
||||
|
||||
private static final int MAX_P5 = 413;
|
||||
private static final int MAX_P2 = 65;
|
||||
private static final long LONG_SIGN_MASK = (1L << 63);
|
||||
private static final BigInteger FIVE = BigInteger.valueOf(5);
|
||||
private static final FDBigInteger MUTABLE_ZERO = new FDBigInteger(0);
|
||||
private static final FDBigInteger IMMUTABLE_ZERO = new FDBigInteger(0).makeImmutable();
|
||||
private static final FDBigInteger IMMUTABLE_MILLION = genMillion1().makeImmutable();
|
||||
private static final FDBigInteger IMMUTABLE_TEN18 = genTen18().makeImmutable();
|
||||
|
||||
private static BigInteger toBigInteger(FDBigInteger v) {
|
||||
return new BigInteger(v.toByteArray());
|
||||
}
|
||||
|
||||
private static FDBigInteger mutable(String hex, int offset) {
|
||||
byte[] chars = new BigInteger(hex, 16).toString().getBytes(StandardCharsets.US_ASCII);
|
||||
return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
|
||||
}
|
||||
|
||||
private static BigInteger biPow52(int p5, int p2) {
|
||||
return FIVE.pow(p5).shiftLeft(p2);
|
||||
}
|
||||
|
||||
// data.length == 1, nWords == 1, offset == 0
|
||||
private static FDBigInteger genMillion1() {
|
||||
return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
|
||||
}
|
||||
|
||||
// data.length == 2, nWords == 1, offset == 0
|
||||
private static FDBigInteger genMillion2() {
|
||||
return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
|
||||
}
|
||||
|
||||
// data.length == 2, nWords == 2, offset == 0
|
||||
private static FDBigInteger genTen18() {
|
||||
return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
|
||||
}
|
||||
|
||||
private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
|
||||
if (!expected.equals(toBigInteger(actual))) {
|
||||
throw new Exception(message + " result " + actual + " expected " + expected.toString(16));
|
||||
}
|
||||
}
|
||||
|
||||
private static void testValueOfPow52(int p5, int p2) throws Exception {
|
||||
check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
|
||||
"valueOfPow52(" + p5 + "," + p2 + ")");
|
||||
}
|
||||
|
||||
private static void testValueOfPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
testValueOfPow52(p5, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
|
||||
BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
|
||||
if (value < 0) {
|
||||
bi = bi.setBit(63);
|
||||
}
|
||||
check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
|
||||
"valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52(long value, int p5) throws Exception {
|
||||
testValueOfMulPow52(value, p5, 0);
|
||||
testValueOfMulPow52(value, p5, 1);
|
||||
testValueOfMulPow52(value, p5, 30);
|
||||
testValueOfMulPow52(value, p5, 31);
|
||||
testValueOfMulPow52(value, p5, 33);
|
||||
testValueOfMulPow52(value, p5, 63);
|
||||
}
|
||||
|
||||
private static void testValueOfMulPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
testValueOfMulPow52(0xFFFFFFFFL, p5);
|
||||
testValueOfMulPow52(0x123456789AL, p5);
|
||||
testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
|
||||
testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
FDBigInteger r = t.leftShift(shift);
|
||||
if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
|
||||
throw new Exception("leftShift doesn't reuse its argument");
|
||||
}
|
||||
if (isImmutable) {
|
||||
check(bt, t, "leftShift corrupts its argument");
|
||||
}
|
||||
check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
|
||||
}
|
||||
|
||||
private static void testLeftShift() throws Exception {
|
||||
testLeftShift(IMMUTABLE_ZERO, 0, true);
|
||||
testLeftShift(IMMUTABLE_ZERO, 10, true);
|
||||
testLeftShift(MUTABLE_ZERO, 0, false);
|
||||
testLeftShift(MUTABLE_ZERO, 10, false);
|
||||
|
||||
testLeftShift(IMMUTABLE_MILLION, 0, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 1, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 12, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 13, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 32, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 33, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 44, true);
|
||||
testLeftShift(IMMUTABLE_MILLION, 45, true);
|
||||
|
||||
testLeftShift(genMillion1(), 0, false);
|
||||
testLeftShift(genMillion1(), 1, false);
|
||||
testLeftShift(genMillion1(), 12, false);
|
||||
testLeftShift(genMillion1(), 13, false);
|
||||
testLeftShift(genMillion1(), 25, false);
|
||||
testLeftShift(genMillion1(), 26, false);
|
||||
testLeftShift(genMillion1(), 32, false);
|
||||
testLeftShift(genMillion1(), 33, false);
|
||||
testLeftShift(genMillion1(), 44, false);
|
||||
testLeftShift(genMillion1(), 45, false);
|
||||
|
||||
testLeftShift(genMillion2(), 0, false);
|
||||
testLeftShift(genMillion2(), 1, false);
|
||||
testLeftShift(genMillion2(), 12, false);
|
||||
testLeftShift(genMillion2(), 13, false);
|
||||
testLeftShift(genMillion2(), 25, false);
|
||||
testLeftShift(genMillion2(), 26, false);
|
||||
testLeftShift(genMillion2(), 32, false);
|
||||
testLeftShift(genMillion2(), 33, false);
|
||||
testLeftShift(genMillion2(), 44, false);
|
||||
testLeftShift(genMillion2(), 45, false);
|
||||
}
|
||||
|
||||
private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
BigInteger bs = toBigInteger(s);
|
||||
int q = t.quoRemIteration(s);
|
||||
BigInteger[] qr = bt.divideAndRemainder(bs);
|
||||
if (!BigInteger.valueOf(q).equals(qr[0])) {
|
||||
throw new Exception("quoRemIteration returns incorrect quo");
|
||||
}
|
||||
check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
|
||||
}
|
||||
|
||||
private static void testQuoRemIteration() throws Exception {
|
||||
// IMMUTABLE_TEN18 == 0de0b6b3a7640000
|
||||
// q = 0
|
||||
testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
|
||||
// q = 1 -> q = 0
|
||||
testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
|
||||
// q = 1
|
||||
testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
|
||||
testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
|
||||
// q = 18
|
||||
testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
|
||||
}
|
||||
|
||||
private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
BigInteger bo = toBigInteger(o);
|
||||
int cmp = t.cmp(o);
|
||||
int bcmp = bt.compareTo(bo);
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("cmp returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "cmp corrupts this");
|
||||
check(bo, o, "cmp corrupts other");
|
||||
if (o.cmp(t) != -cmp) {
|
||||
throw new Exception("asymmetrical cmp");
|
||||
}
|
||||
check(bt, t, "cmp corrupts this");
|
||||
check(bo, o, "cmp corrupts other");
|
||||
}
|
||||
|
||||
private static void testCmp() throws Exception {
|
||||
testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
|
||||
testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
|
||||
testCmp(mutable("5", 0), mutable("6", 0));
|
||||
testCmp(mutable("5", 0), mutable("5", 0));
|
||||
testCmp(mutable("5000000001", 0), mutable("500000001", 0));
|
||||
testCmp(mutable("5000000001", 0), mutable("6", 1));
|
||||
testCmp(mutable("5000000001", 0), mutable("5", 1));
|
||||
testCmp(mutable("5000000000", 0), mutable("5", 1));
|
||||
}
|
||||
|
||||
private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
|
||||
FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
|
||||
BigInteger bt = toBigInteger(t);
|
||||
BigInteger bo = biPow52(p5, p2);
|
||||
int cmp = t.cmp(o);
|
||||
int bcmp = bt.compareTo(bo);
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("cmp returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "cmp corrupts this");
|
||||
check(bo, o, "cmpPow5 corrupts other");
|
||||
}
|
||||
|
||||
private static void testCmpPow52() throws Exception {
|
||||
testCmpPow52(mutable("00000002", 1), 0, 31);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 32);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 33);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 34);
|
||||
testCmpPow52(mutable("00000002", 1), 0, 64);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 32);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 33);
|
||||
testCmpPow52(mutable("00000003", 1), 0, 34);
|
||||
}
|
||||
|
||||
private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
BigInteger bx = toBigInteger(x);
|
||||
BigInteger by = toBigInteger(y);
|
||||
int cmp = t.addAndCmp(x, y);
|
||||
int bcmp = bt.compareTo(bx.add(by));
|
||||
if (bcmp != cmp) {
|
||||
throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
|
||||
}
|
||||
check(bt, t, "addAndCmp corrupts this");
|
||||
check(bx, x, "addAndCmp corrupts x");
|
||||
check(by, y, "addAndCmp corrupts y");
|
||||
}
|
||||
|
||||
private static void testAddAndCmp() throws Exception {
|
||||
testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
|
||||
testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
|
||||
testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
|
||||
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
|
||||
|
||||
testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
|
||||
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
|
||||
testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
|
||||
}
|
||||
|
||||
private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
FDBigInteger r = t.multBy10();
|
||||
if ((bt.signum() == 0 || !isImmutable) && r != t) {
|
||||
throw new Exception("multBy10 of doesn't reuse its argument");
|
||||
}
|
||||
if (isImmutable) {
|
||||
check(bt, t, "multBy10 corrupts its argument");
|
||||
}
|
||||
check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
|
||||
}
|
||||
|
||||
private static void testMultBy10() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
testMultBy10(value, false);
|
||||
value.makeImmutable();
|
||||
testMultBy10(value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
|
||||
BigInteger bt = toBigInteger(t);
|
||||
FDBigInteger r = t.multByPow52(p5, p2);
|
||||
if (bt.signum() == 0 && r != t) {
|
||||
throw new Exception("multByPow52 of doesn't reuse its argument");
|
||||
}
|
||||
check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
|
||||
}
|
||||
|
||||
private static void testMultByPow52() throws Exception {
|
||||
for (int p5 = 0; p5 <= MAX_P5; p5++) {
|
||||
for (int p2 = 0; p2 <= MAX_P2; p2++) {
|
||||
// This strange way of creating a value ensures that it is mutable.
|
||||
FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
|
||||
testMultByPow52(value, p5, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testValueOfPow52();
|
||||
testValueOfMulPow52();
|
||||
testLeftShift();
|
||||
testQuoRemIteration();
|
||||
testCmp();
|
||||
testCmpPow52();
|
||||
testAddAndCmp();
|
||||
// Uncomment the following for more comprehensize but slow testing.
|
||||
// testMultBy10();
|
||||
// testMultByPow52();
|
||||
}
|
||||
}
|
||||
193
test/micro/org/openjdk/bench/java/lang/FloatingPointParse.java
Normal file
193
test/micro/org/openjdk/bench/java/lang/FloatingPointParse.java
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
package org.openjdk.bench.java.lang;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OperationsPerInvocation;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 3, time = 3)
|
||||
@Measurement(iterations = 3, time = 3)
|
||||
@Fork(value = 3)
|
||||
public class FloatingPointParse {
|
||||
|
||||
private static final int N = 1_000_000;
|
||||
private static final int M = 100_000;
|
||||
|
||||
private String[] doubleToString, floatToString, s1, s2, s4, s10;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
Random r = new Random();
|
||||
|
||||
doubleToString = new String[N];
|
||||
for (int i = 0; i < doubleToString.length;) {
|
||||
double v = Double.longBitsToDouble(r.nextLong());
|
||||
if (Double.isFinite(v)) {
|
||||
doubleToString[i++] = Double.toString(v);
|
||||
}
|
||||
}
|
||||
|
||||
floatToString = new String[N];
|
||||
for (int i = 0; i < floatToString.length;) {
|
||||
float v = Float.intBitsToFloat(r.nextInt());
|
||||
if (Float.isFinite(v)) {
|
||||
floatToString[i++] = Float.toString(v);
|
||||
}
|
||||
}
|
||||
|
||||
s1 = new String[M];
|
||||
for (int i = 0; i < s1.length; ++i) {
|
||||
String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
|
||||
s1[i] = f + "e" + (r.nextInt(600) - 300);
|
||||
}
|
||||
|
||||
s2 = new String[M];
|
||||
for (int i = 0; i < s2.length; ++i) {
|
||||
String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
|
||||
s2[i] = f + "e" + (r.nextInt(600) - 300);
|
||||
}
|
||||
|
||||
s4 = new String[M];
|
||||
for (int i = 0; i < s4.length; ++i) {
|
||||
String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
|
||||
s4[i] = f + "e" + (r.nextInt(600) - 300);
|
||||
}
|
||||
|
||||
s10 = new String[M];
|
||||
for (int i = 0; i < s10.length; ++i) {
|
||||
String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
|
||||
+ (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
|
||||
s10[i] = f + "e" + (r.nextInt(600) - 300);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(N)
|
||||
public void parseDoubleToString(Blackhole bh) {
|
||||
for (String s : doubleToString) {
|
||||
bh.consume(Double.parseDouble(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseDoubleS1(Blackhole bh) {
|
||||
for (String s : s1) {
|
||||
bh.consume(Double.parseDouble(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseDoubleS2(Blackhole bh) {
|
||||
for (String s : s2) {
|
||||
bh.consume(Double.parseDouble(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseDoubleS4(Blackhole bh) {
|
||||
for (String s : s4) {
|
||||
bh.consume(Double.parseDouble(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseDoubleS10(Blackhole bh) {
|
||||
for (String s : s10) {
|
||||
bh.consume(Double.parseDouble(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(N)
|
||||
public void parseFloatToString(Blackhole bh) {
|
||||
for (String s : floatToString) {
|
||||
bh.consume(Float.parseFloat(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseFloatS1(Blackhole bh) {
|
||||
for (String s : s1) {
|
||||
bh.consume(Float.parseFloat(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseFloatS2(Blackhole bh) {
|
||||
for (String s : s2) {
|
||||
bh.consume(Float.parseFloat(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseFloatS4(Blackhole bh) {
|
||||
for (String s : s4) {
|
||||
bh.consume(Float.parseFloat(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(M)
|
||||
public void parseFloatS10(Blackhole bh) {
|
||||
for (String s : s10) {
|
||||
bh.consume(Float.parseFloat(s));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user