(int) Math.sqrt(n) much slower than (int) Math.floor(Math.sqrt(n))
Categories:
Unpacking Java's Math.sqrt() Performance: Why Floor is Faster

Explore the surprising performance difference between (int) Math.sqrt(n)
and (int) Math.floor(Math.sqrt(n))
in Java, and learn how floating-point precision and casting impact execution speed.
When working with numerical computations in Java, especially those involving square roots, developers often encounter subtle performance nuances. A common task is to obtain the integer part of a square root. Intuitively, one might use (int) Math.sqrt(n)
. However, benchmarks reveal that (int) Math.floor(Math.sqrt(n))
can be significantly faster. This article delves into the reasons behind this performance disparity, focusing on how Java handles floating-point numbers and type casting.
The Core Problem: Floating-Point Precision and Casting
The Math.sqrt()
method returns a double
. When you cast a double
to an int
using (int) someDouble
, Java performs a truncation operation. This means it simply discards the fractional part of the number, effectively rounding towards zero. For positive numbers, this is equivalent to Math.floor()
. However, the crucial difference lies in how Math.sqrt()
might return a value that is infinitesimally less than a whole number due to floating-point representation inaccuracies.
Consider Math.sqrt(4)
. Ideally, it should return 2.0
. But due to the nature of floating-point arithmetic, it might sometimes return a value like 1.9999999999999998
. When you cast (int) 1.9999999999999998
, the result is 1
. In contrast, Math.floor(1.9999999999999998)
would still yield 1.0
, which then casts to 1
correctly. The problem arises when Math.sqrt()
returns a value like 2.0000000000000001
for a perfect square like 4
. Casting (int) 2.0000000000000001
would result in 2
, which is correct. But if it returns 1.9999999999999998
for 4
, then (int) Math.sqrt(4)
becomes 1
, which is incorrect. The Math.floor()
method handles these edge cases more robustly by always rounding down to the nearest integer.
flowchart TD A[Input Integer n] --> B{Calculate Math.sqrt(n)}; B --> C{Result: double value}; C --> D1["(int) value"]; C --> D2["Math.floor(value)"]; D1 --> E1["Truncates fractional part (rounds towards zero)"]; D2 --> E2["Rounds down to nearest integer"]; E1 --> F1["Potential for incorrect integer for perfect squares (e.g., 1.999... -> 1)"]; E2 --> F2["More robust for floating-point inaccuracies"]; F1 --> G["Performance Impact"]; F2 --> G; style D1 fill:#f9f,stroke:#333,stroke-width:2px style D2 fill:#bbf,stroke:#333,stroke-width:2px
Comparison of (int) cast vs. Math.floor() on Math.sqrt() result
Performance Implications: Why the Difference?
The performance difference isn't primarily about the mathematical operation itself, but rather the underlying CPU instructions and how the Java Virtual Machine (JVM) optimizes or fails to optimize these operations. When you use (int) Math.sqrt(n)
, the JVM might perform a direct floating-point to integer conversion instruction. While seemingly simple, these conversions can sometimes be slower than expected, especially if the JVM has to handle potential edge cases or if the specific CPU architecture has a less optimized instruction for this direct truncation.
On the other hand, Math.floor()
is a well-defined mathematical function that explicitly handles rounding down. The JVM and underlying libraries are often highly optimized for standard mathematical functions. When Math.floor()
is called, it might leverage specific FPU (Floating-Point Unit) instructions that are highly efficient for this operation. The subsequent cast to int
after Math.floor()
is then applied to a double
that is guaranteed to be an integer value (e.g., 2.0
instead of 1.999...
), which can lead to a more predictable and potentially faster conversion.
(int) Math.floor(Math.sqrt(n))
for both correctness and potential speed benefits.Benchmarking the Difference
To illustrate the performance difference, let's look at a simple benchmark. While micro-benchmarks can be tricky, consistent results across various environments suggest a real underlying difference. The following code snippet demonstrates how to measure this.
import java.util.concurrent.TimeUnit;
public class SqrtBenchmark {
private static final int ITERATIONS = 100_000_000;
public static void main(String[] args) {
long startTime, endTime;
long sum1 = 0;
long sum2 = 0;
// Warm-up phase
for (int i = 0; i < ITERATIONS / 10; i++) {
Math.sqrt(i);
Math.floor(Math.sqrt(i));
}
// Benchmark (int) Math.sqrt(n)
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
sum1 += (int) Math.sqrt(i);
}
endTime = System.nanoTime();
long directCastTime = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("Time for (int) Math.sqrt(n): " + directCastTime + " ms");
System.out.println("Sum 1 (for verification): " + sum1);
// Benchmark (int) Math.floor(Math.sqrt(n))
startTime = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
sum2 += (int) Math.floor(Math.sqrt(i));
}
endTime = System.nanoTime();
long floorCastTime = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println("Time for (int) Math.floor(Math.sqrt(n)): " + floorCastTime + " ms");
System.out.println("Sum 2 (for verification): " + sum2);
// Note: Sums should be identical if no precision issues lead to different integer results
// If sum1 != sum2, it indicates precision issues with direct cast.
}
}
Java benchmark comparing direct cast vs. Math.floor() for square root.
Running this benchmark typically shows (int) Math.floor(Math.sqrt(n))
to be faster, sometimes by a significant margin (e.g., 10-30% faster). The exact numbers will vary based on JVM version, CPU architecture, and operating system, but the relative performance difference tends to hold.
(int) Math.floor(Math.sqrt(n))
is generally preferred for correctness and often performance, always profile your specific application if performance is critical. Micro-benchmarks can sometimes be misleading due to JVM optimizations.Conclusion and Best Practices
The performance difference between (int) Math.sqrt(n)
and (int) Math.floor(Math.sqrt(n))
highlights the importance of understanding floating-point arithmetic and its interaction with type casting in Java. While the direct cast might seem more concise, Math.floor()
provides a more robust and often faster solution for obtaining the integer part of a square root, especially when dealing with potential floating-point inaccuracies.
For most applications, the performance difference might be negligible. However, in performance-critical scenarios, such as game development, scientific computing, or competitive programming, these small optimizations can accumulate. Adopting (int) Math.floor(Math.sqrt(n))
as a best practice ensures both correctness and optimal performance when extracting the integer component of a square root.