views:

3151

answers:

5

Closed as Exact Duplicate:

Fastest way to determine if an integer’s square root is an integer

What's a way to see if a number is a perfect square?

bool IsPerfectSquare(long input)
{
   // TODO
}

I'm using C# but this is language agnostic.

Bonus points for clarity and simplicity (this isn't meant to be code-golf).


Edit: This got much more complex than I expected! It turns out the problems with double precision manifest themselves a couple ways. First, Math.Sqrt takes a double which can't precisely hold a long (thanks Jon).

Second, a double's precision will lose small values ( .000...00001) when you have a huge, near perfect square. e.g., my implementation failed this test for Math.Pow(10,18)+1 (mine reported true).

+20  A: 
bool IsPerfectSquare(long input)
{
    long closestRoot = (long) Math.Sqrt(input);
    return input == closestRoot * closestRoot;
}

This may get away from some of the problems of just checking "is the square root an integer" but possibly not all. You potentially need to get a little bit funkier:

bool IsPerfectSquare(long input)
{
    double root = Math.Sqrt(input);

    long rootBits = BitConverter.DoubleToInt64Bits(root);
    long lowerBound = (long) BitConverter.Int64BitsToDouble(rootBits-1);
    long upperBound = (long) BitConverter.Int64BitsToDouble(rootBits+1);

    for (long candidate = lowerBound; candidate <= upperBound; candidate++)
    {
         if (candidate * candidate == input)
         {
             return true;
         }
    }
    return false;
}

Icky, and unnecessary for anything other than really large values, but I think it should work...

Jon Skeet
Looks as if you were faster than me ;-) Oops - your solution is also better, because 'closestRoot' is a far mor accurate name.
Treb
+1 for beating me to it. :)
Bill the Lizard
Love the number of upvotes I got *before* correcting it to a more bulletproof solution ;)
Jon Skeet
dude, you're jon skeet.
Epaga
@Jon: I'm thinking about rescinding my upvote. He asked for *clarity* and *simplicity*. :)
Bill the Lizard
I assumed accuracy was important. Otherwise I'd have gone for the "guaranteed to be random" kind of response :)
Jon Skeet
What's an example input that fails with your first method?
Michael Haren
Ah, I get it now. Math.Pow(10,18)+1 will be true with mine...
Michael Haren
+1  A: 
bool IsPerfectSquare(long input)
{
    long SquareRoot = (long) Math.Sqrt(input);
    return ((SquareRoot * SquareRoot) == input);
}
Treb
+5  A: 

There was a very similar question. Please refer to http://stackoverflow.com/questions/295579/fastest-way-to-determine-if-an-integers-square-root-is-an-integer for an excellent answer.

Totophil
That question was looking for the fastest way. This questions is looking for the clearest code. I think that qualifies to make them different enough.
Bill the Lizard
Bill the Lizard, you've got a strong point here.
Totophil
If this gets a couple more upvotes I'll close my own question! I hadn't seen that one.
Michael Haren
+3  A: 

In Common Lisp, I use the following:

(defun perfect-square-p (n)
  (= (square (isqrt n))
     n))
Svante
+2  A: 

To the solution you choose, don't forget to prepend a quick check for negativeness.

angus
Yes, I had that in there but removed it for brevity. Thanks for pointing it out though
Michael Haren