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



7

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

п & (- п) == п

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


2
я имел в виду, что условие истинно для n = 0, хотя это не степень двойки
FReeze FRancis

это работает с преобразованиями, которые происходят, если n беззнаковое?
Джозеф Гарвин

6

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

if(1==__builtin_popcount(n))

Неа. Я только что это проверил. Мне нравится 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));

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 .


Это, конечно, не самый простой способ, как указано в 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) целым числом.


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.