Years ago I wrote about a fast way to test whether an integer n is a square. The algorithm rules out a few numbers that cannot be squares based on their last (hexidecimal) digit. If the the integer passes through this initial screening, the algorithm takes the square root of n as a floating point number, rounds to an integer, and tests whether the square of the rest equals n.
What’s wrong with the old algorithm
The algorithm described above works fine if n is not too large. However, it implicitly assumes that the integer can be represented exactly as a floating point number. This is two assumptions: (1) it’s not too large to represent, and (2) the representation is exact.
A standard 64-bit floating point number has 1 bit for the sign, 11 bits for the exponent, and 52 bits for the significand. (More on that here.) Numbers will overflow if they run out of exponent bits, and they’ll lose precision if they run out of significand bits. So, for example, 21024 will overflow [1]. And although 253 + 1 will not overflow, it cannot be represented exactly.
These are illustrated in Python below.
>>> float(2**1024) OverflowError: int too large to convert to float >>> n = 2**53 >>> float(n) == float(n + 1) True
A better algorithm
The following algorithm [2] uses only integer operations, and so it will work exactly for arbitrarily large integers in Python. It’s a discrete analog of Newton’s square root algorithm.
def intsqrt(N): n = N while n**2 > N: n = (n + N//n) // 2 return n def issquare(N): n = intsqrt(N) return n**2 == N
The function issquare
will test whether N is a square. The function intsqrt
is broken out as a separate function because it is independently useful since it returns ⌊√N⌋.
The code above correctly handles examples that the original algorithm could not.
>>> issquare(2**1024) True >>> issquare(2**54) True >>> issquare(2**54 + 1) False
You could speed up issquare
by quickly returning False
for numbers that cannot be squared on account of their final digits (in some base—maybe you’d like to use a base larger than 16) as in the original post.
[1] The largest allowed exponent is 1023 for reasons explained here.
[2] Bob Johnson. A Route to Roots. The Mathematical Gazette, Vol. 74, No. 469 (Oct., 1990), pp. 284–285