The cast technique is much better than it may seem to be at first sight.
Conversion from int to double is exact.
Math.sqrt is specified, for normal positive numbers, to return "the double value closest to the true mathematical square root of the argument value". If the input was a perfect square int, the result will be an integer-valued double that is in the int range.
Similarly, conversion of that integer-valued double back to an int is also exact.
The net effect is that the cast technique does not introduce any rounding error in your situation.
If your program would otherwise benefit from use of the Guava intMath API, you can use that to do the square root, but I would not add dependency on an API just to avoid the cast.
Answer from Patricia Shanahan on Stack OverflowThe cast technique is much better than it may seem to be at first sight.
Conversion from int to double is exact.
Math.sqrt is specified, for normal positive numbers, to return "the double value closest to the true mathematical square root of the argument value". If the input was a perfect square int, the result will be an integer-valued double that is in the int range.
Similarly, conversion of that integer-valued double back to an int is also exact.
The net effect is that the cast technique does not introduce any rounding error in your situation.
If your program would otherwise benefit from use of the Guava intMath API, you can use that to do the square root, but I would not add dependency on an API just to avoid the cast.
You can always use the Google Guava IntMath API.
final int sqrtX = IntMath.sqrt(x, RoundingMode.HALF_EVEN);
for loop - Integer square root in java - Stack Overflow
How can I find the Square Root of a Java BigInteger? - Stack Overflow
algorithm - Computing integer square roots in Java - follow-up - Code Review Stack Exchange
java - (int) Math.sqrt(n) much slower than (int) Math.floor(Math.sqrt(n)) - Stack Overflow
Videos
I'm a beginner and came to calculating square root of a number part. Lesson says I need to use double with Math.sqrtint number = 42;double squareRoot = Math.sqrt(number);
and I was curious why we can't use int or float. I searched on google but couldn't find an answer.
tl;dr - skip to the bottom. Use a do/while loop.
How do we get to using a do/while loop? I suggest splitting the "break" condition from your loop. Right now it's more complicated to understand what is happening (which is probably why you are confused).
Something like:
int curSum = 0;
for (i=1; i<num; i +=2){
if (curSum + i >= num) {
break;
}
++y;
curSum +=i;
}
Is much more clear. It's easier to read and understand what is actually happening in your loop logic.
You can read through the for loop and nearly exactly, in English, read
- "iterate i, by multiples of 2 (starting at 1, so all odd numbers). Count the number of times you can sum these until the current sum plus the next value is greater than the input number."
Along this line, I would suggest a bit better variable names:
import java.util.Scanner;
public class IntRoot{
public static void main(String[] args){
int num;
System.out.print("Enter a non-negative integer: ");
Scanner sc = new Scanner(System.in);
num = sc.nextInt();
int i;
int iterations=1;
int curSum = 0;
for (i=1; i<num; i +=2){
if (curSum + i >= num) {
break;
}
iterations++;
curSum +=i;
}
System.out.print(iterations);
}
}
Last, the question is... given your conditions, are you really "iterate over every number up to your input number?" to which the answer is, "no, you want to iterate until a condition is met."
There are better loop structures for this, such as a do-while loop:
import java.util.Scanner;
public class IntRoot{
public static void main(String[] args){
int num;
System.out.print("Enter a non-negative integer: ");
Scanner sc = new Scanner(System.in);
num = sc.nextInt();
int i=1;
int iterations=1;
int curSum = 0;
do {
i+=2;
iterations++;
curSum +=i;
} while (curSum + i < num);
System.out.print(iterations);
}
}
Your for loop is just counting:
for (i=1; num>=i+(i+2); i +=2){
…
}
Iterates with i having the values of 1, 3, 5, 7, 9, …
If you want to have the values 1, 1+3, 1+3+5, 1+3+5+7, … in your loop you have to do something like this:
sum = 1;
for (i = 1; num >= sum; i += 2) {
sum += i;
…
}
Just for fun:
public static BigInteger sqrt(BigInteger x) {
BigInteger div = BigInteger.ZERO.setBit(x.bitLength()/2);
BigInteger div2 = div;
// Loop until we hit the same value twice in a row, or wind
// up alternating.
for(;;) {
BigInteger y = div.add(x.divide(div)).shiftRight(1);
if (y.equals(div) || y.equals(div2))
return y;
div2 = div;
div = y;
}
}
I know of no library solution for your question. You'll have to import an external library solution from somewhere. What I give you below is less complicated than getting an external library.
You can create your own external library solution in a class with two static methods as shown below and add that to your collection of external libraries. The methods don't need to be instance methods and so they are static and, conveniently, you don't have to instance the class to use them. The norm for integer square roots is a floor value (i.e. the largest integer less than or equal to the square root), so you may need only the one static method, the floor method, in the class below for the floor value and can choose to ignore the ceiling (i.e. the smallest integer greater than or equal to the square root) method version. Right now, they are in the default package, but you can add a package statement to put them in whatever package you find convenient.
The methods are dirt simple and the iterations converge to the closest integer answer very, very fast. They throw an IllegalArgumentException if you try to give them a negative argument. You can change the exception to another one, but you must ensure that a negatve argument throws some kind of exception or at least doesn't attempt the computation. Integer square roots of negative numbers don't exist since we are not in the realm of imaginary numbers.
These come from very well known simple iterative integer square root algorithms that have been used in hand computations for centuries. It works by averaging an overestimate and underestimate to converge to a better estimate. This may be repeated until the estimate is as close as is desired.
They are based on y1 = ((x/y0) + y0) / 2 converging to the largest integer, yn, where yn * yn <= x.
This will give you a floor value for a BigInteger square root, y, of x where y * y <= x and (y + 1) * (y + 1) > x.
An adaptation can give you a ceiling value for BigInteger square root, y, of x where y * y >= x and (y - 1) * (y - 1) < x
Both methods have been tested and work. They are here:
import java.math.BigInteger;
public class BigIntSqRoot {
public static BigInteger bigIntSqRootFloor(BigInteger x)
throws IllegalArgumentException {
if (x.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Negative argument.");
}
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x .equals(BigInteger.ZERO) || x.equals(BigInteger.ONE)) {
return x;
} // end if
BigInteger two = BigInteger.valueOf(2L);
BigInteger y;
// starting with y = x / 2 avoids magnitude issues with x squared
for (y = x.divide(two);
y.compareTo(x.divide(y)) > 0;
y = ((x.divide(y)).add(y)).divide(two));
return y;
} // end bigIntSqRootFloor
public static BigInteger bigIntSqRootCeil(BigInteger x)
throws IllegalArgumentException {
if (x.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Negative argument.");
}
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x == BigInteger.ZERO || x == BigInteger.ONE) {
return x;
} // end if
BigInteger two = BigInteger.valueOf(2L);
BigInteger y;
// starting with y = x / 2 avoids magnitude issues with x squared
for (y = x.divide(two);
y.compareTo(x.divide(y)) > 0;
y = ((x.divide(y)).add(y)).divide(two));
if (x.compareTo(y.multiply(y)) == 0) {
return y;
} else {
return y.add(BigInteger.ONE);
}
} // end bigIntSqRootCeil
} // end class bigIntSqRoot
When using function parameters, use the primitive types when available:
Function<Long, Long> function
is a red-flag, and should be LongUnaryOperator.
Your code will spin in to an infinite loop for 25% of all long values .... anything larger than Long.MAX_VALUE/4 will cause this loop to become infinite:
// Do the exponential search. while (4 * sqrt * sqrt <= number) { sqrt *= 2; }
About that loop.... why do you have a magic number 4....? What does it do?
This code needs more testing... and magic numbers need to be removed.
You want something fast and efficient.
But did you really check what this method does :
public static long intSqrt1(long number) { long sqrt = 0L; while ((sqrt + 1) * (sqrt + 1) <= number) { sqrt++; } return sqrt; }
Your adding 1 to sqrt 3 times.
I don't see any reason why you should do that, but I'm guessing it's for the easy part for returning sqrt.
Let's refactor just this to a more efficient method.
First of all, a while loop where you need to count your steps, that's called a for loop.
public static long intSqrt1(long number) {
long sqrt;
for (sqrt = 1; (sqrt * sqrt) <= number; sqrt++) {}
return --sqrt;
}
This method is doing all the same but I do raise the sqrt only once each time and if I return it, I will decrease it.
Now I did write some basic test, it's not a how a real performance test should be but in this case you will see the difference because it's big :
public static void main(String[] args) {
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
intSqrt1(902545489); // new one
}
long midTime = System.nanoTime();
for (int j = 0; j < 10000; j++) {
intSqrt2(902545489); // old one
}
long endTime = System.nanoTime();
System.out.println((midTime - startTime) + " vs " + (endTime - midTime));
}
As you can see, the for I initialize 2 time a new integer and I put the new method first because we can have a delay with the startup so there could be time faults in the first method.
Still I got this as output: (I put dot's for easy reading)
175.509.799 vs 360.087.176
As you can see I halved the time.
The Math.floor method just delegates the call to the StrictMath.floor method (as seen on java.lang.StrictMath's source code). This method is a native method. After this method the cast does not have to do anything because it is already a number that is equal to an integer (so no decimal places).
Maybe the native implementation of floor is faster than the cast of a double value to an int value.
I cannot reproduce the same results. Using this simple Java code below, the function without the call to Math.floor is consistently faster:
with floor elapsed milliseconds: 7354
without floor elapsed milliseconds: 4252
public class TestCast {
private static final int REPS = Integer.MAX_VALUE / 4;
private static void withFloor() {
long sum = 0;
long start = System.currentTimeMillis();
for (int i = REPS; i != 0; --i) {
sum += (int)Math.floor(Math.sqrt(i));
}
long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("with floor elapsed milliseconds: " + elapsed);
System.out.println(sum);
}
private static void withoutFloor() {
long sum = 0;
long start = System.currentTimeMillis();
for (int i = REPS; i != 0; --i) {
sum += (int)Math.sqrt(i);
}
long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("without floor elapsed milliseconds: " + elapsed);
System.out.println(sum);
}
public static void main(String[] args) {
withFloor();
withoutFloor();
}
}
Also, looking at the disassembled byte code we can clearly see the call to Math.floor in the first function and no call in the second. There must be something else going on in your code. Perhaps you can post your code or a shortened version of it that shows the results that you are seeing.
private static void withFloor();
Code:
0: lconst_0
1: lstore_0
2: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
5: lstore_2
6: ldc #3 // int 536870911
8: istore 4
10: iload 4
12: ifeq 35
15: lload_0
16: iload 4
18: i2d
19: invokestatic #4 // Method java/lang/Math.sqrt:(D)D
22: invokestatic #5 // Method java/lang/Math.floor:(D)D
25: d2i
26: i2l
27: ladd
28: lstore_0
29: iinc 4, -1
32: goto 10
35: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
38: lstore 4
40: lload 4
42: lload_2
43: lsub
44: lstore 6
46: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
49: new #7 // class java/lang/StringBuilder
52: dup
53: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
56: ldc #9 // String with floor elapsed milliseconds:
58: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
61: lload 6
63: invokevirtual #11 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
66: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
69: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
72: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
75: lload_0
76: invokevirtual #14 // Method java/io/PrintStream.println:(J)V
79: return
private static void withoutFloor();
Code:
0: lconst_0
1: lstore_0
2: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
5: lstore_2
6: ldc #3 // int 536870911
8: istore 4
10: iload 4
12: ifeq 32
15: lload_0
16: iload 4
18: i2d
19: invokestatic #4 // Method java/lang/Math.sqrt:(D)D
22: d2i
23: i2l
24: ladd
25: lstore_0
26: iinc 4, -1
29: goto 10
32: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
35: lstore 4
37: lload 4
39: lload_2
40: lsub
41: lstore 6
43: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
46: new #7 // class java/lang/StringBuilder
49: dup
50: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
53: ldc #15 // String without floor elapsed milliseconds:
55: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
58: lload 6
60: invokevirtual #11 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
63: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
66: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
69: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
72: lload_0
73: invokevirtual #14 // Method java/io/PrintStream.println:(J)V
76: return