Я дам вам самый быстрый способ для x86-64 на момент написания и общую технику, если у вас есть быстрый пол, который работает для аргументов <2 ^ 63 </strong>, если вы заботитесь ополный диапазон, затем см. ниже.
Я удивлен низким качеством других ответов, потому что они говорят вам, как получить слово, но преобразуют пол очень дорогим способом (используя условные выражения и все!), чтобыпотолок.
Если вы можете быстро получить пол логарифма, например, с помощью __builtin_clzll
, то пол очень легко получить следующим образом:
unsigned long long log2Floor(unsigned long long x) {
return 63 - __builtin_clzll(x);
}
unsigned long long log2Ceiling(unsigned long long x) {
return log2Floor(2*x - 1);
}
Это работает, потому чтоон добавляет 1 к результату , если x не является степенью 2 .
См. разницу в ассемблере x86-64 в проводнике компилятора для другой реализации потолка, подобногоэто:
auto log2CeilingDumb(unsigned long x) {
return log2Floor(x) + (!!(x & (x - 1)));
}
Дает:
log2Floor(unsigned long): # @log2Floor(unsigned long)
bsr rax, rdi
ret
log2CeilingDumb(unsigned long): # @log2CeilingDumb(unsigned long)
bsr rax, rdi
lea rcx, [rdi - 1]
and rcx, rdi
cmp rcx, 1
sbb eax, -1
ret
log2Ceiling(unsigned long): # @log2Ceiling(unsigned long)
lea rax, [rdi + rdi]
add rax, -1
bsr rax, rax
ret
Для полного диапазона, это в предыдущем ответе: return log2Floor(x - 1) + 1
, это значительно медленнее, так как он использует в x86-64 четыреинструкции по сравнению с тремяБова.