BAEL-6828: Finding the Square Root of a BigInteger in Java (#14903)
* BAEL-6828: Finding the Square Root of a BigInteger in Java * BAEL-6828: Remove Java 9 annotation from the test * BAEL-6828: Remove Java 9 profile
This commit is contained in:
parent
7c6b5ac3de
commit
d76083ae04
|
@ -25,8 +25,12 @@
|
||||||
<version>${commons-codec}</version>
|
<version>${commons-codec}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>core-java-numbers-6</finalName>
|
<finalName>core-java-numbers-6</finalName>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -39,5 +43,6 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<commons-codec>1.15</commons-codec>
|
<commons-codec>1.15</commons-codec>
|
||||||
|
<guava.version>32.1.2-jre</guava.version>
|
||||||
</properties>
|
</properties>
|
||||||
</project>
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.baeldung.bigintegerroot;
|
||||||
|
|
||||||
|
public class BenchmarkRunner {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
org.openjdk.jmh.Main.main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.baeldung.bigintegerroot;
|
||||||
|
|
||||||
|
public class BigIntegerHolder {
|
||||||
|
|
||||||
|
private BigIntegerHolder() {
|
||||||
|
}
|
||||||
|
public static final String BIG_NUMBER = "179769313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
public static final String VERY_BIG_NUMBER = "32473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834";
|
||||||
|
public static final String INSANELY_BIG_NUMBER = "3247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834324739274923749279342847923749237492374987230480128343247392749237492793428479237492374923749872304801283432473927492374927934284792374923749237498723048012834";
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.baeldung.bigintegerroot;
|
||||||
|
|
||||||
|
import static com.baeldung.bigintegerroot.BigIntegerHolder.*;
|
||||||
|
|
||||||
|
import com.baeldung.bigintegerroot.algorithms.Newton;
|
||||||
|
import com.baeldung.bigintegerroot.algorithms.NewtonPlus;
|
||||||
|
import com.google.common.math.BigIntegerMath;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
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.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
|
import org.openjdk.jmh.annotations.State;
|
||||||
|
import org.openjdk.jmh.annotations.Warmup;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
@Warmup(iterations = 1)
|
||||||
|
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.MINUTES)
|
||||||
|
@Fork(1)
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
public class BigIntegerSquareRootBenchmark {
|
||||||
|
|
||||||
|
@Param({BIG_NUMBER, VERY_BIG_NUMBER, INSANELY_BIG_NUMBER})
|
||||||
|
public String number;
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void calculateRootWithJava(Blackhole blackhole) {
|
||||||
|
final BigInteger integer = new BigInteger(number);
|
||||||
|
final BigInteger root = integer.sqrt();
|
||||||
|
blackhole.consume(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void calculateRootWithGuava(Blackhole blackhole) {
|
||||||
|
final BigInteger integer = new BigInteger(number);
|
||||||
|
final BigInteger root = BigIntegerMath.sqrt(integer, RoundingMode.DOWN);
|
||||||
|
blackhole.consume(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void calculateRootWithNewtonPlus(Blackhole blackhole) {
|
||||||
|
final BigInteger integer = new BigInteger(number);
|
||||||
|
final BigInteger root = NewtonPlus.sqrt(integer);
|
||||||
|
blackhole.consume(root);
|
||||||
|
}
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void calculateRootWithNewton(Blackhole blackhole) {
|
||||||
|
final BigInteger integer = new BigInteger(number);
|
||||||
|
final BigInteger root = Newton.sqrt(integer);
|
||||||
|
blackhole.consume(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.bigintegerroot.algorithms;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class Newton {
|
||||||
|
|
||||||
|
private Newton() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger sqrt(BigInteger n) {
|
||||||
|
// Initial approximation
|
||||||
|
BigInteger x = n.divide(BigInteger.TWO);
|
||||||
|
|
||||||
|
// Tolerance level (small positive integer)
|
||||||
|
BigInteger tolerance = BigInteger.ONE;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// x_new = 0.5 * (x + n / x)
|
||||||
|
BigInteger xNew = x.add(n.divide(x)).divide(BigInteger.TWO);
|
||||||
|
|
||||||
|
// Check for convergence within tolerance
|
||||||
|
if (x.subtract(xNew).abs().compareTo(tolerance) <= 0) {
|
||||||
|
return xNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = xNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package com.baeldung.bigintegerroot.algorithms;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
public class NewtonPlus {
|
||||||
|
|
||||||
|
private NewtonPlus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// A fast square root by Ryan Scott White.
|
||||||
|
public static BigInteger sqrt(BigInteger x) {
|
||||||
|
if (x.compareTo(BigInteger.valueOf(144838757784765629L)) < 0) {
|
||||||
|
long xAsLong = x.longValue();
|
||||||
|
long vInt = (long)Math.sqrt(xAsLong);
|
||||||
|
if (vInt * vInt > xAsLong)
|
||||||
|
vInt--;
|
||||||
|
return BigInteger.valueOf(vInt); }
|
||||||
|
|
||||||
|
double xAsDub = x.doubleValue();
|
||||||
|
BigInteger val;
|
||||||
|
if (xAsDub < 2.1267e37) // 2.12e37 largest here
|
||||||
|
// since sqrt(long.max*long.max) > long.max
|
||||||
|
{
|
||||||
|
long vInt = (long)Math.sqrt(xAsDub);
|
||||||
|
val = BigInteger.valueOf
|
||||||
|
((vInt + x.divide(BigInteger.valueOf(vInt)).longValue()) >> 1);
|
||||||
|
}
|
||||||
|
else if (xAsDub < 4.3322e127) {
|
||||||
|
// Convert a double to a BigInteger
|
||||||
|
long bits = Double.doubleToLongBits(Math.sqrt(xAsDub));
|
||||||
|
int exp = ((int) (bits >> 52) & 0x7ff) - 1075;
|
||||||
|
val = BigInteger.valueOf((bits & ((1L << 52)) - 1) | (1L << 52)).shiftLeft(exp);
|
||||||
|
|
||||||
|
val = x.divide(val).add(val).shiftRight(1);
|
||||||
|
if (xAsDub > 2e63) {
|
||||||
|
val = x.divide(val).add(val).shiftRight(1); }
|
||||||
|
}
|
||||||
|
else // handle large numbers over 4.3322e127
|
||||||
|
{
|
||||||
|
int xLen = x.bitLength();
|
||||||
|
int wantedPrecision = ((xLen + 1) / 2);
|
||||||
|
int xLenMod = xLen + (xLen & 1) + 1;
|
||||||
|
|
||||||
|
//////// Do the first Sqrt on Hardware ////////
|
||||||
|
long tempX = x.shiftRight(xLenMod - 63).longValue();
|
||||||
|
double tempSqrt1 = Math.sqrt(tempX);
|
||||||
|
long valLong = Double.doubleToLongBits(tempSqrt1) & 0x1fffffffffffffL;
|
||||||
|
|
||||||
|
if (valLong == 0)
|
||||||
|
valLong = 1L << 53;
|
||||||
|
|
||||||
|
//////// Classic Newton Iterations ////////
|
||||||
|
val = BigInteger.valueOf(valLong).shiftLeft(53 - 1)
|
||||||
|
.add((x.shiftRight(xLenMod -
|
||||||
|
(3 * 53))).divide(BigInteger.valueOf(valLong)));
|
||||||
|
|
||||||
|
int size = 106;
|
||||||
|
for (; size < 256; size <<= 1) {
|
||||||
|
val = val.shiftLeft(size - 1).add(x.shiftRight
|
||||||
|
(xLenMod - (3*size)).divide(val));}
|
||||||
|
|
||||||
|
if (xAsDub > 4e254) { // 4e254 = 1<<845.77
|
||||||
|
int numOfNewtonSteps = 31 -
|
||||||
|
Integer.numberOfLeadingZeros(wantedPrecision / size)+1;
|
||||||
|
|
||||||
|
////// Apply Starting Size ////////
|
||||||
|
int wantedSize = (wantedPrecision >> numOfNewtonSteps) + 2;
|
||||||
|
int needToShiftBy = size - wantedSize;
|
||||||
|
val = val.shiftRight(needToShiftBy);
|
||||||
|
|
||||||
|
size = wantedSize;
|
||||||
|
do {
|
||||||
|
//////// Newton Plus Iteration ////////
|
||||||
|
int shiftX = xLenMod - (3 * size);
|
||||||
|
BigInteger valSqrd = val.multiply(val).shiftLeft(size - 1);
|
||||||
|
BigInteger valSU = x.shiftRight(shiftX).subtract(valSqrd);
|
||||||
|
val = val.shiftLeft(size).add(valSU.divide(val));
|
||||||
|
size *= 2;
|
||||||
|
} while (size < wantedPrecision);
|
||||||
|
}
|
||||||
|
val = val.shiftRight(size - wantedPrecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect a round ups. This function can be further optimized - see article.
|
||||||
|
// For a ~7% speed bump the following line can be removed but round-ups will occur.
|
||||||
|
if (val.multiply(val).compareTo(x) > 0)
|
||||||
|
val = val.subtract(BigInteger.ONE);
|
||||||
|
|
||||||
|
// Enabling the below will guarantee an error is stopped for larger numbers.
|
||||||
|
// Note: As of this writing, there are no known errors.
|
||||||
|
BigInteger tmp = val.multiply(val);
|
||||||
|
if (tmp.compareTo(x) > 0) {
|
||||||
|
System.out.println("val^2(" + val.multiply(val).toString()
|
||||||
|
+ ") ≥ x(" + x.toString()+")");
|
||||||
|
System.console().readLine();
|
||||||
|
//throw new Exception("Sqrt function had internal error - value too high");
|
||||||
|
}
|
||||||
|
if (tmp.add(val.shiftLeft(1)).add(BigInteger.ONE).compareTo(x) <= 0) {
|
||||||
|
System.out.println("(val+1)^2("
|
||||||
|
+ val.add(BigInteger.ONE).multiply(val.add(BigInteger.ONE)).toString()
|
||||||
|
+ ") ≥ x(" + x.toString() + ")");
|
||||||
|
System.console().readLine();
|
||||||
|
//throw new Exception("Sqrt function had internal error - value too low");
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.baeldung.bigintegerroot;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import com.baeldung.bigintegerroot.algorithms.Newton;
|
||||||
|
import com.baeldung.bigintegerroot.algorithms.NewtonPlus;
|
||||||
|
import com.google.common.math.BigIntegerMath;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.math3.util.Pair;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||||
|
import org.junit.jupiter.api.condition.JRE;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
class BigIntegerSquareRootUnitTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
BigIntegerHolder.BIG_NUMBER,
|
||||||
|
BigIntegerHolder.VERY_BIG_NUMBER,
|
||||||
|
BigIntegerHolder.VERY_BIG_NUMBER
|
||||||
|
})
|
||||||
|
void squareRootTest(String number) {
|
||||||
|
final BigInteger bigInteger = new BigInteger(number);
|
||||||
|
final BigInteger javaRoot = bigInteger.sqrt();
|
||||||
|
final BigInteger guavaRoot = BigIntegerMath.sqrt(bigInteger, RoundingMode.DOWN);
|
||||||
|
final BigInteger newtonRoot = Newton.sqrt(bigInteger);
|
||||||
|
final BigInteger newtonPlusRoot = NewtonPlus.sqrt(bigInteger);
|
||||||
|
|
||||||
|
assertTrue(Stream.of(
|
||||||
|
new Pair<>(javaRoot, guavaRoot),
|
||||||
|
new Pair<>(guavaRoot, newtonRoot),
|
||||||
|
new Pair<>(newtonRoot, newtonPlusRoot)
|
||||||
|
).allMatch(pair -> pair.getFirst().equals(pair.getSecond())));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue