Для этой проблемы есть простой трюк:
bool IsPowerOfTwo(ulong x)
{
return (x & (x - 1)) == 0;
}
Обратите внимание, эта функция выдаст true
для 0
, что не является степенью 2
. Если вы хотите исключить это, вот как:
bool IsPowerOfTwo(ulong x)
{
return (x != 0) && ((x & (x - 1)) == 0);
}
Объяснение
Прежде всего побитовый двоичный оператор & из определения MSDN:
Двоичные & операторы предопределены для целочисленных типов и bool. За
целочисленные типы & вычисляют логическое побитовое И своих операндов.
Для булевых операндов & вычисляет логическое И своих операндов; тот
результат равен true, если и только если оба его операнда имеют значение true.
Теперь давайте посмотрим, как все это закончится:
Функция возвращает логическое значение (true / false) и принимает один входящий параметр типа unsigned long (в данном случае x). Давайте для простоты предположим, что кто-то передал значение 4 и вызвал функцию следующим образом:
bool b = IsPowerOfTwo(4)
Теперь мы заменим каждое вхождение x на 4:
return (4 != 0) && ((4 & (4-1)) == 0);
Ну, мы уже знаем, что 4! = 0 - это правда, пока все хорошо. Но как насчет:
((4 & (4-1)) == 0)
Это, конечно, означает:
((4 & 3) == 0)
Но что именно 4&3
?
Двоичное представление 4 равно 100, а двоичное представление 3 - 011 (помните, что & принимает двоичное представление этих чисел). Итак имеем:
100 = 4
011 = 3
Представьте, что эти значения складываются во многом как элементарное сложение. Оператор &
говорит, что если оба значения равны 1, то результат равен 1, в противном случае он равен 0. Итак, 1 & 1 = 1
, 1 & 0 = 0
, 0 & 0 = 0
и 0 & 1 = 0
. Итак, мы делаем математику:
100
011
----
000
Результат просто 0. Итак, мы вернемся и посмотрим, что наш оператор возврата теперь переводит:
return (4 != 0) && ((4 & 3) == 0);
Что теперь означает:
return true && (0 == 0);
return true && true;
Мы все знаем, что true && true
- это просто true
, и это показывает, что для нашего примера 4 - это степень 2.