Який найпростіший спосіб перевірити, чи число є ступенем 2 в 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 ++ тепер ми можемо використовувати власні характеристики з процесорами з підтримкою ІМТ, що видає машинну інструкцію робити це за один раз. Уявляю, у Java є й інші речі, як, можливо, щось із рутинної математики.
jww

Відповіді:


190

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

http://www.graphics.stanford.edu/~seander/bithacks.html має велику колекцію розумних алгоритмів обертання бітів, включаючи цей.


8
так в основному(n>0 && ((n & (n-1)) == 0))
Саураб Гоял

1
@SaurabhGoyal або n && !(n & (n - 1))як вказано у посиланні у відповіді.
Карстен,

Чому, ну чому, це не вгорі відповідей? OP, будь ласка, прийміть.
donturner

@SaurabhGoyal Одне невелике вдосконалення полягає в наступному: n & !(n & (n - 1)). Зверніть увагу на побітове І &(не логічне та &&). Побітові оператори не реалізують коротке замикання і, отже, код не розгалужується. Це переважно в ситуаціях, коли ймовірні неправильні передбачення гілок і при обчисленні rhs виразу (тобто !(n & (n - 1))) є дешевим.
Кассіо Нері

@cassio !є логічним оператором і, отже, значення !(n & (n - 1))буде булевим значенням. Ви впевнені, що булеве значення і число можуть бути надані побітовому оператору І? Якщо так, це виглядає добре.
Saurabh Goyal

81

Ступінь двох матиме лише один біт (для беззнакових чисел). Щось на зразок

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

Буде працювати нормально; одиниця, менша за ступінь двох, дорівнює 1 в менш значущих бітах, тому повинна І до 0 побітових.

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


Вам не вистачає "!" або '== 0'

Ви також пропустили тест на від’ємне значення 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 & (x1));
}

Детальніше про двійничко дивіться тут .


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:

Побітовий 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



7

для будь-якої степені 2 також виконується наступне.

n & (- n) == n

ПРИМІТКА: Умова відповідає n = 0, хоча вона не дорівнює 2.
Причина, чому це працює:
-n є доповненням 2s до n. -n матиме кожен біт ліворуч від самого праворуч встановленого біта n, перевернутий порівняно з n. Для степенів 2 існує лише один встановлений біт.


2
я мав на увазі, що умова відповідає n = 0, хоча це не сила двох
FReeze FRancis

чи вдається це з перетвореннями, які відбуваються, якщо n не підписано?
Джозеф Гарвін

5

У C ++ 20 є, std::ispow2що ви можете використовувати саме для цієї мети, якщо вам не потрібно реалізовувати це самостійно:

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

5

Це, мабуть, найшвидше, якщо використовувати GCC. Він використовує лише інструкцію процесора POPCNT та одне порівняння. Бінарне подання будь-якої потужності 2 числа завжди має лише один біт, інші біти завжди дорівнюють нулю. Отже, ми підраховуємо кількість встановлених бітів за допомогою POPCNT, і якщо воно дорівнює 1, число дорівнює степеню 2. Я не думаю, що є якісь можливі швидші методи. І це дуже просто, якщо ви це зрозуміли одного разу:

if(1==__builtin_popcount(n))

Ні. Я щойно перевірив це. Я люблю popcount, але для тесту power-of-2 тест i && !(i & (i - 1)))на моїй машині швидший приблизно на 10%, навіть тоді, коли я був впевнений, що ввімкнув інструкцію POPCNT для рідної збірки в gcc.
eraoul

Ой, беру назад. Моя тестова програма працювала в циклі, і передбачення гілок було "обманом". Ви маєте рацію, якщо на вашому процесорі є інструкція POPCNT, це швидше.
eraoul

3

Далі було б швидше, ніж найбільш голосована відповідь через логічне коротке замикання та той факт, що порівняння відбувається повільно.

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

Якщо ви знаєте, що x не може бути 0, тоді

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


3

Який найпростіший спосіб перевірити, чи число є ступенем 2 в C ++?

Якщо у вас є сучасний процесор Intel з інструкціями щодо маніпулювання бітами , ви можете виконати наступне. Він опускає прямий код C / C ++, оскільки на нього вже відповіли інші, але він потрібен, якщо ІМТ недоступний або ввімкнений.

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__. Він доступний у компіляторах 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;? Це повинно повернутися будь- boolколи. Навіть потрійний оператор ІМО був би зрозумілішим, якщо хтось справді хоче дотримуватися його int.
luk32

0

Я знаю, що це дуже стара публікація, але я подумав, що може бути цікаво розмістити її тут.


Від Code-Golf SE (отже, честь тому, хто написав це): Вітрина мов

(Абзац про С , фрагмент довжини підпункту 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.
Анонім

-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.