Эффективное специальное решение Microsoft (например, Visual Studio 2017) на C / C ++ для целочисленного ввода. Обрабатывает случай ввода, точно совпадающего со степенью двойки, уменьшая его перед проверкой местоположения старшего значащего 1 бита.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, Value - 1);
return (1U << (Index + 1));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if defined(WIN64) // The _BitScanReverse64 intrinsic is only available for 64 bit builds because it depends on x64
inline unsigned long long ExpandToPowerOf2(unsigned long long Value)
{
unsigned long Index;
_BitScanReverse64(&Index, Value - 1);
return (1ULL << (Index + 1));
}
#endif
Это генерирует 5 или около того встроенных инструкций для процессора Intel, аналогичных следующим:
dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl
По-видимому, компилятор Visual Studio C ++ не предназначен для оптимизации этого для значений времени компиляции, но он не похож на множество инструкций.
Edit:
Если вы хотите, чтобы входное значение 1 приводило к 1 (2 к нулевой степени), небольшая модификация приведенного выше кода все еще генерирует прямые инструкции без ветвления.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, --Value);
if (Value == 0)
Index = (unsigned long) -1;
return (1U << (Index + 1));
}
Создает еще несколько инструкций. Хитрость в том, что Index можно заменить тестом, сопровождаемым инструкцией cmove.