How to check if a number is a power of 2

后端 未结 25 1524
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 03:30

Today I needed a simple algorithm for checking if a number is a power of 2.

The algorithm needs to be:

  1. Simple
  2. Correct for any ulong
相关标签:
25条回答
  • 2020-11-22 03:53

    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);
    }
    

    Explanation

    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.

    0 讨论(0)
  • 2020-11-22 03:53
    bool IsPowerOfTwo(ulong x)
    {
        return x > 0 && (x & (x - 1)) == 0;
    }
    
    0 讨论(0)
  • 2020-11-22 03:53
        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;
        }
    
    0 讨论(0)
  • 2020-11-22 03:55

    Example

    0000 0001    Yes
    0001 0001    No
    

    Algorithm

    1. Using a bit mask, divide NUM the variable in binary

    2. IF R > 0 AND L > 0: Return FALSE

    3. Otherwise, NUM becomes the one that is non-zero

    4. IF NUM = 1: Return TRUE

    5. Otherwise, go to Step 1

    Complexity

    Time ~ O(log(d)) where d is number of binary digits

    0 讨论(0)
  • 2020-11-22 03:56

    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;
    }
    
    
    0 讨论(0)
  • 2020-11-22 03:57

    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
    
    0 讨论(0)
提交回复
热议问题