Today I needed a simple algorithm for checking if a number is a power of 2.
The algorithm needs to be:
ulong
There's a simple trick for this problem:
bool IsPowerOfTwo(ulong x)
{
return (x & (x - 1)) == 0;
}
Note, this function will report true
for 0
, which is not a power of 2
. If you want to exclude that, here's how:
bool IsPowerOfTwo(ulong x)
{
return (x != 0) && ((x & (x - 1)) == 0);
}
First and foremost the bitwise binary & operator from MSDN definition:
Binary & operators are predefined for the integral types and bool. For integral types, & computes the logical bitwise AND of its operands. For bool operands, & computes the logical AND of its operands; that is, the result is true if and only if both its operands are true.
Now let's take a look at how this all plays out:
The function returns boolean (true / false) and accepts one incoming parameter of type unsigned long (x, in this case). Let us for the sake of simplicity assume that someone has passed the value 4 and called the function like so:
bool b = IsPowerOfTwo(4)
Now we replace each occurrence of x with 4:
return (4 != 0) && ((4 & (4-1)) == 0);
Well we already know that 4 != 0 evals to true, so far so good. But what about:
((4 & (4-1)) == 0)
This translates to this of course:
((4 & 3) == 0)
But what exactly is 4&3
?
The binary representation of 4 is 100 and the binary representation of 3 is 011 (remember the & takes the binary representation of these numbers). So we have:
100 = 4
011 = 3
Imagine these values being stacked up much like elementary addition. The &
operator says that if both values are equal to 1 then the result is 1, otherwise it is 0. So 1 & 1 = 1
, 1 & 0 = 0
, 0 & 0 = 0
, and 0 & 1 = 0
. So we do the math:
100
011
----
000
The result is simply 0. So we go back and look at what our return statement now translates to:
return (4 != 0) && ((4 & 3) == 0);
Which translates now to:
return true && (0 == 0);
return true && true;
We all know that true && true
is simply true
, and this shows that for our example, 4 is a power of 2.
bool IsPowerOfTwo(ulong x)
{
return x > 0 && (x & (x - 1)) == 0;
}
bool IsPowerOfTwo(int n)
{
if (n > 1)
{
while (n%2 == 0)
{
n >>= 1;
}
}
return n == 1;
}
And here's a general algorithm for finding out if a number is a power of another number.
bool IsPowerOf(int n,int b)
{
if (n > 1)
{
while (n % b == 0)
{
n /= b;
}
}
return n == 1;
}
Example
0000 0001 Yes
0001 0001 No
Algorithm
Using a bit mask, divide NUM
the variable in binary
IF R > 0 AND L > 0: Return FALSE
Otherwise, NUM
becomes the one that is non-zero
IF NUM = 1: Return TRUE
Otherwise, go to Step 1
Complexity
Time ~ O(log(d))
where d
is number of binary digits
in this approach , you can check if there is only 1 set bit in the integer and the integer is > 0 (c++).
bool is_pow_of_2(int n){
int count = 0;
for(int i = 0; i < 32; i++){
count += (n>>i & 1);
}
return count == 1 && n > 0;
}
In C, I tested the i && !(i & (i - 1)
trick and compared it with __builtin_popcount(i)
, using gcc on Linux, with the -mpopcnt flag to be sure to use the CPU's POPCNT instruction. My test program counted the # of integers between 0 and 2^31 that were a power of two.
At first I thought that i && !(i & (i - 1)
was 10% faster, even though I verified that POPCNT was used in the disassembly where I used__builtin_popcount
.
However, I realized that I had included an if statement, and branch prediction was probably doing better on the bit twiddling version. I removed the if and POPCNT ended up faster, as expected.
Results:
Intel(R) Core(TM) i7-4771 CPU max 3.90GHz
Timing (i & !(i & (i - 1))) trick
30
real 0m13.804s
user 0m13.799s
sys 0m0.000s
Timing POPCNT
30
real 0m11.916s
user 0m11.916s
sys 0m0.000s
AMD Ryzen Threadripper 2950X 16-Core Processor max 3.50GHz
Timing (i && !(i & (i - 1))) trick
30
real 0m13.675s
user 0m13.673s
sys 0m0.000s
Timing POPCNT
30
real 0m13.156s
user 0m13.153s
sys 0m0.000s
Note that here the Intel CPU seems slightly slower than AMD with the bit twiddling, but has a much faster POPCNT; the AMD POPCNT doesn't provide as much of a boost.
popcnt_test.c:
#include "stdio.h"
// Count # of integers that are powers of 2 up to 2^31;
int main() {
int n;
for (int z = 0; z < 20; z++){
n = 0;
for (unsigned long i = 0; i < 1<<30; i++) {
#ifdef USE_POPCNT
n += (__builtin_popcount(i)==1); // Was: if (__builtin_popcount(i) == 1) n++;
#else
n += (i && !(i & (i - 1))); // Was: if (i && !(i & (i - 1))) n++;
#endif
}
}
printf("%d\n", n);
return 0;
}
Run tests:
gcc popcnt_test.c -O3 -o test.exe
gcc popcnt_test.c -O3 -DUSE_POPCNT -mpopcnt -o test-popcnt.exe
echo "Timing (i && !(i & (i - 1))) trick"
time ./test.exe
echo
echo "Timing POPCNT"
time ./test-opt.exe