Вы можете использовать логарифмы. Быстрый поиск Google для «fast log2 C++» привел довольно длинный список идей. Тогда ваш ответ будет log2 (x)/2, и вам нужно будет найти способ убедиться, что ваш результат будет целым числом, если вам нужен только ответ для точной степени 4.
Если вы программируете для процессора x86 вы можете использовать BitScanForward & BitScanReverse, чтобы найти установленный бит и использовать его для вычисления log2. Следующий код работает в Visual Studio, для GCC или других, есть другие способы сделать встроенную сборку.
uint32_t exact_power_of_4_scan(uint32_t num)
{
unsigned long reverse;
unsigned long forward;
if (!_BitScanReverse(&reverse, num)) return 0;
_BitScanForward(&forward, num);
if (reverse != forward) return 0; // makes sure only a single bit is set
if (reverse & 0x1) return 0; // only want every other power of 2
return reverse/2;
}
Если вам требуется переносное решение, поиск таблицы может быть способом, но сложнее.
uint8_t not_single_bit[256] = {
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
uint8_t log2_table[256] = {
0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint32_t exact_power_of_2(uint32_t num)
{
auto a = not_single_bit[num & 0xff];
auto b = not_single_bit[(num >> 8) & 0xff];
auto c = not_single_bit[(num >> 16) & 0xff];
auto d = not_single_bit[(num >> 24) & 0xff];
if (a + b + c + d != 3) {
return 0;
}
if (!a) {
return log2_table[num & 0xff];
}
if (!b) {
return log2_table[(num >> 8) & 0xff] + 8;
}
if (!c) {
return log2_table[(num >> 16) & 0xff] + 16;
}
return log2_table[(num >> 24) & 0xff] + 24;
}
uint32_t exact_power_of_4(uint32_t num)
{
auto ret = exact_power_of_2(num);
if (ret & 0x1) return 0;
return ret/2;
}
Оба являются линейными алгоритмами. Первый, вероятно, будет бить цикл для почти любого значения num
, но я его не тестировал. Второй, вероятно, только хорош для довольно больших num
s.
Приятная мысль, она немного дикая, но она избавляется от ветвящегося цикла и, вероятно, более эффективна при высоких значениях n. Добавьте код в свой ответ, и я соглашусь с ним: int temp = BSR (n); value =! (n & ((1 << temp) -1)) * (temp >> 1); – user1043761
BSR = бит сканирование назад – user1043761