Question
I want the fastest way to determine whether a long value is a perfect square in Java, meaning its square root is also an integer.
My current implementation uses Math.sqrt():
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
long test = (long) (Math.sqrt(n) + 0.5);
return test * test == n;
}
I am wondering whether there is a faster approach that works only in the integer domain, instead of relying on floating-point math.
A full lookup table is not practical because there are too many possible values for long inputs.
This function is called millions of times in performance-sensitive code, such as Project Euler problems, so even small optimizations may matter.
I also tested several alternatives:
Math.sqrt()without adding0.5still seemed correct in my tests.- Fast inverse square root was faster, but it became incorrect for larger values.
- Newton's method was slower than
Math.sqrt(). - Integer-only Newton variants needed overflow workarounds and were still slower.
- Binary search was also slower.
- Small modular pre-checks using
switchor bitwise filtering helped avoid unnecessary square root calls. - A boolean lookup table for small modular checks was slightly slower in Java because of array bounds checking.
So the real underlying question is: what is the most practical and fast way to test whether a Java long is a perfect square, especially when the function is called very frequently?
Short Answer
By the end of this page, you will understand how to check whether a long is a perfect square in Java, why Math.sqrt() is usually the best baseline, how integer-based filtering can speed things up, and what common correctness and overflow issues to avoid.
Concept
A perfect square is a number that can be written as k * k for some integer k.
Examples:
0 = 0 * 01 = 1 * 14 = 2 * 29 = 3 * 316 = 4 * 4
For a Java long, the task is:
- Reject negative numbers immediately.
- Compute a candidate square root.
- Check whether squaring that candidate gives the original number.
The most common practical solution is:
long root = (long) Math.sqrt(n);
return root * root == n;
Sometimes people round instead of truncating:
long root = (long) (Math.sqrt(n) + 0.5);
return root * root == n;
Why this matters:
- Perfect square checks appear in math problems, number theory, geometry, game logic, and optimization-heavy code.
- A fast rejection step can save work when most numbers are not squares.
- Correctness is critical because floating-point and overflow mistakes can silently produce wrong answers.
In Java, Math.sqrt() is highly optimized and typically implemented using hardware support. That means purely integer-based approaches are often not actually faster, even if they seem simpler in theory.
So the real lesson is not just how to test for a square, but how to choose a solution that is both correct and practical for performance-sensitive code.
Mental Model
Think of this as a two-stage security check:
- Front desk screening: quickly reject values that could never be perfect squares.
- Identity verification: compute the square root candidate and confirm it exactly.
For example, many numbers can be rejected just by looking at their last few bits. A square number cannot end with every possible bit pattern.
So instead of asking every number to go through the expensive full check, you first ask:
- “Do you even look like a square?”
If the answer is no, return false immediately.
If yes, then do the more expensive exact verification using Math.sqrt() and multiplication.
This is a common performance pattern in programming:
- cheap filter first
- expensive exact check second
Syntax and Examples
The simplest correct Java approach is:
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
long root = (long) Math.sqrt(n);
return root * root == n;
}
How it works
Math.sqrt(n)returns adouble- Casting to
longremoves the decimal part - If
nis a perfect square, thenroot * root == n
Example
System.out.println(isPerfectSquare(16)); // true
System.out.println(isPerfectSquare(15)); // false
System.out.println(isPerfectSquare(0)); // true
System.out.println(isPerfectSquare(-4)); // false
Adding a fast pre-check
You can reject many non-squares before calling Math.sqrt():
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
long lastHexDigit = n & 0xF;
if (lastHexDigit != 0 && lastHexDigit != 1 && lastHexDigit != 4 && lastHexDigit != 9) {
return false;
}
long root = (long) Math.sqrt(n);
return root * root == n;
}
Why this helps
A perfect square in base 16 can only end in:
0149
So many numbers are rejected immediately with a cheap bitwise operation.
More explicit version using switch
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
switch ((int) (n & 0xF)) {
case 0:
case 1:
case 4:
case 9:
long root = (long) Math.sqrt(n);
return root * root == n;
default:
return false;
}
}
This version is often easy to read and makes the filtering step clear.
Step by Step Execution
Consider this version:
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
long root = (long) Math.sqrt(n);
return root * root == n;
}
Now trace it with n = 81.
Step 1
if (n < 0)
81 < 0isfalse- continue
Step 2
long root = (long) Math.sqrt(n);
Math.sqrt(81)gives9.0- cast to
longgives9
Step 3
return root * root == n;
9 * 9is8181 == 81istrue
Result: true
Now trace it with n = 80.
Step 1
80 < 0isfalse
Step 2
Math.sqrt(80)is about8.944...- cast to
longgives8
Step 3
8 * 8is6464 == 80isfalse
Result: false
Now trace the filtered version with n = 82:
public static boolean isPerfectSquare(long n) {
if (n < 0) {
return false;
}
long lastHexDigit = n & 0xF;
if (lastHexDigit != 0 && lastHexDigit != 1 && lastHexDigit != 4 && lastHexDigit != 9) {
return false;
}
long root = (long) Math.sqrt(n);
return root * root == n;
}
Step 1
82 < 0isfalse
Step 2
long lastHexDigit = n & 0xF;
82in hexadecimal is0x52- last hex digit is
2
Step 3
Check whether 2 is one of 0, 1, 4, 9
- it is not
- return
falseimmediately
No square root calculation is needed.
Real World Use Cases
Perfect square checks are more common than they first appear.
Number theory and coding challenges
- Project Euler problems
- prime-related formulas
- integer sequence generation
- factor pair detection
Geometry and grids
- checking whether an area can form an exact square
- verifying square dimensions in tile or board problems
- detecting integer distances in coordinate problems
Data processing
- validating that a value matches square-based constraints
- checking matrix sizes such as
n x n - converting a flat array length into a square grid
Example:
int cells = 144;
if (isPerfectSquare(cells)) {
int size = (int) Math.sqrt(cells);
System.out.println("Grid is " + size + " x " + size);
}
Game and simulation logic
- square map generation
- board sizing
- validating player-built structures
Performance-sensitive filters
In math-heavy programs, a square test is often used as a gate before doing more expensive work.
Real Codebase Usage
In real projects, developers usually combine this concept with a few common patterns.
1. Guard clauses
Reject impossible cases early:
if (n < 0) {
return false;
}
This keeps the function simple and avoids nested logic.
2. Cheap pre-validation
Use modular or bitwise checks before expensive operations:
int last = (int) (n & 0xF);
if (last != 0 && last != 1 && last != 4 && last != 9) {
return false;
}
This is common in high-throughput code where most inputs fail.
3. Exact verification after approximation
Even if the root comes from floating-point math, the final test should be exact:
long root = (long) Math.sqrt(n);
return root * root == n;
This pattern appears in many algorithms:
- estimate first
- verify exactly after
4. Utility methods
This logic is often placed in a small helper class:
public final class MathUtils {
private MathUtils() {}
public static boolean isPerfectSquare(long n) {
if (n < 0) return false;
long root = (long) Math.sqrt(n);
return root * root == n;
}
}
5. Benchmark before micro-optimizing
In real codebases, developers measure actual performance instead of assuming integer-only code is faster. JVM optimizations and hardware instructions often make Math.sqrt() surprisingly hard to beat.
Common Mistakes
1. Forgetting negative inputs
Broken code:
public static boolean isPerfectSquare(long n) {
long root = (long) Math.sqrt(n);
return root * root == n;
}
Problem:
Math.sqrt()of a negative number isNaN- casting
NaNtolongdoes not express your intent clearly
Better:
if (n < 0) {
return false;
}
2. Trusting floating-point without exact verification
Broken code:
return Math.sqrt(n) % 1 == 0;
Problem:
- floating-point values can have rounding issues
- equality checks on floating-point results are risky
Better:
long root = (long) Math.sqrt(n);
return root * root == n;
3. Using only a modular filter
Broken code:
return (n & 0xF) == 0 || (n & 0xF) == 1 || (n & 0xF) == 4 || (n & 0xF) == 9;
Problem:
- this rejects many non-squares
- but it does not prove the number is a square
Example:
25is a square41ends in hex digit9but is not a square
You still need exact verification.
4. Overflow in custom integer algorithms
Broken idea:
long mid = ...;
if (mid * mid == n) {
...
}
Problem:
mid * midcan overflow in many search-based methods- overflow can make comparisons wrong
If writing your own binary search or Newton method, you must design around overflow.
5. Assuming integer-only means faster
This is a very common performance mistake.
In Java:
Math.sqrt()is heavily optimized- integer algorithms may need loops
- loops and overflow checks can cost more than one hardware-accelerated square root call
6. Over-optimizing without measuring
A tiny optimization may make code harder to read without providing meaningful speedup.
Use a benchmark if performance really matters.
Comparisons
| Approach | Correct for all long values? | Usually fast in Java? | Notes |
|---|---|---|---|
Math.sqrt() + exact multiply check | Yes | Yes | Best practical baseline |
Math.sqrt() + modular pre-filter | Yes | Often | Good when many inputs are not squares |
| Binary search integer sqrt | Yes | Usually no | More code, many loop iterations |
| Newton's method | Can be | Usually no | Must handle convergence and overflow carefully |
Floating-point integer test like sqrt(n) % 1 == 0 | Risky | Not recommended | Can fail because of floating-point behavior |
| Full lookup table | No for all long values | Impractical | Too much memory / range too large |
Math.sqrt() vs integer-only methods
| Factor | Math.sqrt() | Integer-only methods |
|---|---|---|
| Simplicity | Very simple | Usually more complex |
| Readability | High | Medium to low |
| Overflow risk | Low in the common pattern | Higher |
| JVM/hardware optimization | Excellent | Depends on implementation |
| Good for micro-optimization | Often surprisingly good | Only if benchmarked |
switch vs if for modular filtering
In Java, there is often little practical difference between these for tiny cases. Choose the one that is clearer unless benchmarking proves otherwise.
Cheat Sheet
Basic perfect square check
public static boolean isPerfectSquare(long n) {
if (n < 0) return false;
long root = (long) Math.sqrt(n);
return root * root == n;
}
Fast reject using last hex digit
int last = (int) (n & 0xF);
if (last != 0 && last != 1 && last != 4 && last != 9) {
return false;
}
Important rules
- Negative numbers are not perfect squares in the integer domain.
- Always do an exact final check:
root * root == n. - Do not rely on
Math.sqrt(n) % 1 == 0. Math.sqrt()is usually the best baseline in Java.- Pre-filters help when most inputs are non-squares.
Common valid endings for squares
In hexadecimal, a square can only end with:
0149
Edge cases
0→true1→true- negative values →
false - very large
longvalues should still use exact verification
Recommended practical version
public static boolean isPerfectSquare(long n) {
if (n < 0) return false;
int last = (int) (n & 0xF);
if (last != 0 && last != 1 && last != 4 && last != 9) {
return false;
}
long root = (long) Math.sqrt(n);
return root * root == n;
}
FAQ
Is Math.sqrt() accurate enough for checking perfect squares in Java?
Yes, when you use it only to get a candidate root and then verify with root * root == n.
Do I need to add 0.5 before casting?
Usually no for this pattern. A truncation-based check is commonly sufficient when followed by exact verification.
Why not use Math.sqrt(n) % 1 == 0?
Because floating-point values can have rounding behavior that makes exact decimal-based checks unreliable.
Is there a faster integer-only algorithm than Math.sqrt()?
Sometimes in theory, but in Java Math.sqrt() is often faster in practice because it is highly optimized and may use hardware instructions.
Why does a bitwise pre-check help?
It quickly rejects values that cannot be squares, so you avoid many square root calls.
Can root * root overflow?
With the common pattern using root = (long) Math.sqrt(n), the root is at most about 3037000499, so root * root stays within long range.
Should I use a lookup array for the modular filter?
Not necessarily. In Java, array access includes bounds checking, so a small if or switch can be just as fast or faster.
Mini Project
Description
Build a small Java utility that checks whether numbers in a list are perfect squares. The project demonstrates a practical pattern used in performance-sensitive code: reject impossible inputs quickly, then perform an exact verification. This mirrors real utility methods that are reused in math-heavy programs.
Goal
Create a Java program that tests multiple long values and prints whether each one is a perfect square.
Requirements
- Write a method that accepts a
longand returnstrueif it is a perfect square. - Reject negative numbers immediately.
- Add a quick bitwise filter before calling
Math.sqrt(). - Test the method with a mix of negative numbers, small values, large values, squares, and non-squares.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.