Question

How to Check if a long Is a Perfect Square in Java

javamathoptimizationperfect-square

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 adding 0.5 still 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 switch or 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 * 0
  • 1 = 1 * 1
  • 4 = 2 * 2
  • 9 = 3 * 3
  • 16 = 4 * 4

For a Java long, the task is:

  1. Reject negative numbers immediately.
  2. Compute a candidate square root.
  3. 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:

  1. Front desk screening: quickly reject values that could never be perfect squares.
  2. 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
Take Quiz

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 a double
  • Casting to long removes the decimal part
  • If n is a perfect square, then root * 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:

  • 0
  • 1
  • 4
  • 9

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 < 0 is false
  • continue

Step 2

long root = (long) Math.sqrt(n);
  • Math.sqrt(81) gives 9.0
  • cast to long gives 9

Step 3

return root * root == n;
  • 9 * 9 is 81
  • 81 == 81 is true

Result: true


Now trace it with n = 80.

Step 1

  • 80 < 0 is false

Step 2

  • Math.sqrt(80) is about 8.944...
  • cast to long gives 8

Step 3

  • 8 * 8 is 64
  • 64 == 80 is false

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 < 0 is false

Step 2

long lastHexDigit = n & 0xF;
  • 82 in hexadecimal is 0x52
  • last hex digit is 2

Step 3

Check whether 2 is one of 0, 1, 4, 9

  • it is not
  • return false immediately

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.

Take Quiz

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 is NaN
  • casting NaN to long does 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:

  • 25 is a square
  • 41 ends in hex digit 9 but 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 * mid can 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

ApproachCorrect for all long values?Usually fast in Java?Notes
Math.sqrt() + exact multiply checkYesYesBest practical baseline
Math.sqrt() + modular pre-filterYesOftenGood when many inputs are not squares
Binary search integer sqrtYesUsually noMore code, many loop iterations
Newton's methodCan beUsually noMust handle convergence and overflow carefully
Floating-point integer test like sqrt(n) % 1 == 0RiskyNot recommendedCan fail because of floating-point behavior
Full lookup tableNo for all long valuesImpracticalToo much memory / range too large

Math.sqrt() vs integer-only methods

FactorMath.sqrt()Integer-only methods
SimplicityVery simpleUsually more complex
ReadabilityHighMedium to low
Overflow riskLow in the common patternHigher
JVM/hardware optimizationExcellentDepends on implementation
Good for micro-optimizationOften surprisingly goodOnly 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.

Take Quiz

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:

  • 0
  • 1
  • 4
  • 9

Edge cases

  • 0 → true
  • 1 → true
  • negative values → false
  • very large long values 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.

Take Quiz

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

  1. Write a method that accepts a long and returns true if it is a perfect square.
  2. Reject negative numbers immediately.
  3. Add a quick bitwise filter before calling Math.sqrt().
  4. Test the method with a mix of negative numbers, small values, large values, squares, and non-squares.
Take Quiz

Keep learning

Related questions

View more