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

94

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

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

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

Муравей
источник
@rootTraveller - Наверное, не дубликат. C ++ и Java - это разные языки, и каждый из них предлагает разные возможности. Например, в C / C ++ теперь мы можем использовать встроенные функции с процессорами с включенным BMI, которые выдают машинную инструкцию, чтобы сделать это за один раз. Я полагаю, что в Java есть и другие вещи, например, что-то из математической процедуры.
jww

Ответы:

191

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

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

Анонимный
источник
8
так в основном(n>0 && ((n & (n-1)) == 0))
Саураб Гойал
1
@SaurabhGoyal или n && !(n & (n - 1))как указано в ссылке в ответе.
Carsten
Почему, ну почему это не первая часть ответов? OP, пожалуйста, примите.
donturner 06
@SaurabhGoyal Одно небольшое усовершенствование заключается в следующем: n & !(n & (n - 1)). Обратите внимание на побитовое И &(не логическое и &&). Побитовые операторы не реализуют короткое замыкание и, следовательно, код не ветвится. Это предпочтительнее в ситуациях, когда вероятны неверные предсказания переходов и когда вычисление правой части выражения (то есть !(n & (n - 1))) обходится дешево.
Cassio Neri
@cassio !- логический оператор, и, следовательно, значение !(n & (n - 1))будет логическим. Вы уверены, что логическое значение и число могут быть присвоены оператору побитового И? Если да, то выглядит хорошо.
Саурабх Гоял
81

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

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

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

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

Адам Райт
источник
Вам не хватает символа '!' или '== 0'
Вам также не хватает теста на отрицательное значение x.
Роб Уэллс,
Отлично, как вы его отредактировали без появления надписи «отредактировано x минут назад»?
Серьезно, как вы получили 120 повторений за явно неправильный ответ?
@Mike F: Действительно, кажется, что люди будут голосовать за ответы, не проверяя их. Думаю, любой может ошибиться - если я допущу что-нибудь в будущем, не стесняйтесь исправлять их.
Адам Райт,
49

Степени двойки в двоичном формате выглядят так:

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

Чтобы узнать больше, смотрите здесь .

Мэтт Хауэллс
источник
14

Подход №1:

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

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

Подход №2:

Побитовое И число с его предыдущим числом должно быть равно НУЛЮ.

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

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

Подход № 3:

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

Пример: Число = 8 Двоичное из 8: 1 0 0 0 Двоичное из 7: 0 1 1 1, а побитовое исключающее ИЛИ обоих чисел равно 1 1 1 1 = 15.

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

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

Радж Диксит
источник
8
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}
Роб Уэллс
источник
7

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

п & (- п) == п

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

FReeze FRancis
источник
2
я имел в виду, что условие истинно для n = 0, хотя это не степень двойки
FReeze FRancis
это работает с преобразованиями, которые происходят, если n беззнаковое?
Джозеф Гарвин
6

Вероятно, это самый быстрый вариант при использовании GCC. Он использует только инструкцию процессора POPCNT и одно сравнение. Двоичное представление любого числа степени двойки всегда имеет только один установленный бит, остальные биты всегда равны нулю. Итак, мы подсчитываем количество установленных битов с помощью POPCNT, и если оно равно 1, то число равно степени 2. Я не думаю, что есть какие-либо возможные более быстрые методы. А это очень просто, если вы однажды поняли:

if(1==__builtin_popcount(n))
F10PPY
источник
Неа. Я только что это проверил. Мне нравится popcount, но для теста power-of-2 тест i && !(i & (i - 1)))на моей машине примерно на 10% быстрее, даже когда я был уверен, что включил встроенную инструкцию POPCNT сборки в gcc.
eraoul
Ой, беру обратно. Моя тестовая программа работала в цикле, и предсказание ветвлений было «обманом». Вы правы, если у вас есть инструкция POPCNT на вашем процессоре, она быстрее.
eraoul
5

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

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));
Ракете1111
источник
3

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

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

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

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x – 1));
}
Маргус
источник
3

Какой самый простой способ проверить, является ли число в 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
}

GCC, ICC и Clang поддерживают BMI сигналов с __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 .

jww
источник
Это, конечно, не самый простой способ, как указано в OP, но, возможно, самый быстрый для конкретных сред. Чрезвычайно полезно продемонстрировать, как создавать условия для разных архитектур.
fearless_fool
1

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

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

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

Джир Джонс
источник
Он может быть не быстрым или коротким, но он правильный, в отличие от основных ответов.
2
На момент комментирования все они были прослушаны. С тех пор они были отредактированы до приемлемого состояния.
0

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

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}
Четан
источник
0

Возможно через c ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}
Джей Понкиа
источник
2
Для меня это непросто и не быстро.
luk32
2
Т.е. это, конечно, не быстро из-за log2, и доказательство того, что это работает, не так-то просто объяснить (именно, можно ли попасться ошибками округления?). Это также излишне запутано с if..return..else..return. Что плохого в том, чтобы его свернуть return x==(double)y;? Он должен вернуть boolanyayws. IMO даже тернарный оператор был бы более ясным, если бы кто-то действительно хотел придерживаться int.
luk32
0

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


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

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

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}
Эрель
источник
-1

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

MikeJ
источник
2
Нет, может быть, об этом :-).
paxdiablo
1
Это привело бы к проблемам с неточностью чисел с плавающей запятой. ln (1 << 29) / ln (2) получается 29,000000000000004.
Anonymous
-3

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

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

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

Джесси Роберж
источник
5
Приятно видеть, как главный ответ на этот вопрос также может быть реализован в T-SQL, но это не имеет отношения к заданному здесь вопросу. Альтернативой (если вы искали решение в T-SQL, нашли этот ответ на вопрос, реализовали его в T-SQL и сочли достаточно интересным опубликовать этот ответ) было бы опубликовать вопрос со ссылкой на T-SQL, тогда ответьте сами, со ссылкой на ответ на этот вопрос. Надеюсь, это предложение окажется полезным.
Саймон
это на самом деле не отвечает на этот вопрос
phuclv