Какой самый простой способ проверить, является ли число степенью 2 в C++?

Мне нужна такая функция:

// return true iff 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  is_power_of_2(3) => false
bool is_power_of_2(int n);

Кто-нибудь может подсказать, как я мог написать это? Можете ли вы сказать мне хороший веб-сайт, где можно найти такой алгоритм?

14 ответов

(n & (n - 1)) == 0 лучший. Однако обратите внимание, что он будет неверно возвращать true для n=0, поэтому, если это возможно, вы захотите проверить это явно.

http://www.graphics.stanford.edu/~seander/bithacks.html имеет большую коллекцию умных алгоритмов переворота битов, включая этот.

Степень два будет иметь только один установленный бит (для чисел без знака). Что-то вроде

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

Будет работать нормально; единица, меньшая, чем степень двойки, равна единице в младших битах, поэтому значение И должно быть равно 0 в битах.

Поскольку я принимал числа без знака, тест == 0 (который я изначально забыл, извините) является адекватным. Вам может потребоваться тест> 0, если вы используете целые числа со знаком.

Сила двух в двоичном виде выглядит так:

1: 0001
2: 0010
4: 0100
8: 1000

Обратите внимание, что всегда установлен ровно 1 бит. Единственное исключение - целое число со знаком. Например, 8-разрядное целое число со знаком со значением -128 выглядит так:

10000000

Поэтому, проверив, что число больше нуля, мы можем использовать хитроумный хак для проверки того, что установлен один и только один бит.

bool is_power_of_2(int x) {
    return x > 0 && !(x & (x−1));
}

Для получения дополнительной информации смотрите здесь.

В C++20 есть std::ispow2 который вы можете использовать именно для этой цели, если вам не нужно реализовывать это самостоятельно:

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));

Подход № 1:

Разделите число на 2, чтобы проверить это.

Временная сложность: O (log2n).

Подход № 2:

Побитовое И число с только что предыдущим номером должно быть равно нулю.

Пример: Число = 8 Двоичное число 8: 1 0 0 0 Двоичное число 7: 0 1 1 1 и побитовое И обоих чисел равно 0 0 0 0 = 0.

Временная сложность: O (1).

Подход № 3:

Побитовое XOR число с только что предыдущим номером должно быть суммой обоих чисел.

Пример: Число = 8 Двоичное число 8: 1 0 0 0 Двоичное число 7: 0 1 1 1 и битовое XOR обоих чисел равно 1 1 1 1 = 15.

Временная сложность: O (1).

http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html

bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}

Для любой степени 2 также справедливо следующее.

п &(- п)== п

ПРИМЕЧАНИЕ: условие истинно для n=0, хотя оно не является степенью 2.
Причина, по которой это работает:
-n является дополнением 2s от n. -n будет иметь каждый бит слева от крайнего правого установленного бита n по сравнению с n. Для степеней 2 есть только один установленный бит.

Это самый быстрый:

if(1==__builtin_popcount(n))

Последующее будет быстрее, чем ответ с наибольшим количеством голосов из-за логического короткого замыкания и факта, что сравнение медленное.

int isPowerOfTwo(unsigned int x)
{
  return x && !(x & (x – 1));
}

Если вы знаете, что х не может быть 0, то

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x – 1));
}
return n > 0 && 0 == (1 << 30) % n;

Какой самый простой способ проверить, является ли число степенью 2 в C++?

Если у вас современный процессор Intel с инструкциями по управлению битами, вы можете выполнить следующее. Он пропускает прямой код C/C++, потому что другие уже ответили на него, но он вам нужен, если BMI недоступен или не включен.

bool IsPowerOf2_32(uint32_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u32(x));
#endif
    // Fallback to C/C++ code
}

bool IsPowerOf2_64(uint64_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u64(x));
#endif
    // Fallback to C/C++ code
}

Поддержка BMI сигналов GCC, ICC и Clang с __BMI__, Он доступен в компиляторах Microsoft в Visual Studio 2015 и выше, когда AVX2 доступен и включен. Для получения необходимых заголовков см. Файлы заголовков для встроенных функций SIMD.

Я обычно охраняю _blsr_u64 с _LP64_ в случае компиляции на i686. Clang нуждается в небольшом обходном пути, потому что он использует несколько иной внутренний символ nam:

#if defined(__GNUC__) && defined(__BMI__)
# if defined(__clang__)
#  ifndef _tzcnt_u32
#   define _tzcnt_u32(x) __tzcnt_u32(x)
#  endif
#  ifndef _blsr_u32
#    define  _blsr_u32(x)  __blsr_u32(x)
#  endif
#  ifdef __x86_64__
#   ifndef _tzcnt_u64
#    define _tzcnt_u64(x) __tzcnt_u64(x)
#   endif
#   ifndef _blsr_u64
#     define  _blsr_u64(x)  __blsr_u64(x)
#   endif
#  endif  // x86_64
# endif  // Clang
#endif  // GNUC and BMI

Можете ли вы сказать мне хороший веб-сайт, где можно найти такой алгоритм?

Этот сайт часто цитируется: Bit Twiddling Hacks.

Это не самый быстрый или короткий путь, но я думаю, что он очень удобочитаемый. Так что я бы сделал что-то вроде этого:

bool is_power_of_2(int n)
  int bitCounter=0;
  while(n) {
    if ((n & 1) == 1) {
      ++bitCounter;
    }
    n >>= 1;
  }
  return (bitCounter == 1);
}

Это работает, так как двоичный код основан на двух степенях. Любое число с только одним установленным битом должно быть степенью двойки.

Вот еще один метод, в этом случае с использованием | вместо &:

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}

Другой способ (возможно, не самый быстрый) - определить, является ли ln(x) / ln(2) целым числом.

Я знаю, что это очень старый пост, но я подумал, что было бы интересно разместить его здесь.


От Code-Golf SE (так что вся заслуга авторам): Showcase of Languages

(Абзац про C, подпункт Длина 36 сниппет)

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

Можно через с ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}

Это метод сдвига битов в T-SQL (SQL Server):

SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo

Это намного быстрее, чем делать логарифм четыре раза (первый набор для получения десятичного результата, второй набор для получения целого числа и сравнения)

Другие вопросы по тегам