Может ли кто-нибудь помочь объяснить, что делает этот один лайнер?
Обычно я могу найти большинство C-кода, но это над моей головой.
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
пример использования будет выглядеть примерно так:
int x = 57;
kroundup32(x);
//x is now 64
Несколько других примеров:
1 к 1
2 до 2
7 до 8
31 до 32
60 до 64
От 3000 до 4096
Я знаю, что он округляет целое число до ближайшей силы 2, но это касается моих знаний.
Любые объяснения будут с благодарностью.
Спасибо
Ответы
Ответ 1
(--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
- Уменьшить x на 1
- ИЛИ x с (x/2).
- ИЛИ x с (x/4).
- ИЛИ x с (x/16).
- ИЛИ x с (x/256).
- ИЛИ x с (x/65536).
- Увеличить x на 1.
Для 32-разрядного целого числа без знака это должно перемещать значение до ближайшей степени 2, которая равна или больше. Секции OR устанавливают все младшие бит ниже наивысшего бита, поэтому он заканчивается как мощность 2 минус один, затем вы добавляете обратно к нему. Похоже, он несколько оптимизирован и, следовательно, не очень читабельен; выполняя его побитовыми операциями и сдвигом битов в одиночку, и как макрос (поэтому нет служебных вызовов функции).
Ответ 2
Побитовые и/или сдвиговые операции по существу устанавливают каждый бит между самым высоким битом набора и битным нулем. Это даст номер формы 2^n - 1
. Окончательный приращение добавляет один, чтобы получить номер формы 2^n
. Начальный декремент гарантирует, что вы не округлите числа, которые уже имеют две силы до следующей мощности, так что, например, 2048 не становится 4096.
Ответ 3
На моей машине kroundup32
дается 6.000 м раундов/сек
И следующая функция дает 7,693 м раундов/сек
inline int scan_msb(int x)
{
#if defined(__i386__) || defined(__x86_64__)
int y;
__asm__("bsr %1, %0"
: "=r" (y)
: "r" (x)
: "flags"); /* ZF */
return y;
#else
#error "Implement me for your platform"
#endif
}
inline int roundup32(int x)
{
if (x == 0) return x;
else {
const int bit = scan_msb(x);
const int mask = ~((~0) << bit);
if (x & mask) return (1 << (bit+1));
else return (1 << bit);
}
}
Итак, @thomasrutter, я не говорю, что он "сильно оптимизирован".
И соответствующая сборка (только значимая часть) (для GCC 4.4.4):
kroundup32:
subl $1, %edi
movl %edi, %eax
sarl %eax
orl %edi, %eax
movl %eax, %edx
sarl $2, %edx
orl %eax, %edx
movl %edx, %eax
sarl $4, %eax
orl %edx, %eax
movl %eax, %edx
sarl $8, %edx
orl %eax, %edx
movl %edx, %eax
sarl $16, %eax
orl %edx, %eax
addl $1, %eax
ret
roundup32:
testl %edi, %edi
movl %edi, %eax
je .L6
movl $-1, %edx
bsr %edi, %ecx
sall %cl, %edx
notl %edx
testl %edi, %edx
jne .L10
movl $1, %eax
sall %cl, %eax
.L6:
rep
ret
.L10:
addl $1, %ecx
movl $1, %eax
sall %cl, %eax
ret
По какой-то причине я не нашел подходящей реализации scan_msb
(например, #define scan_msb(x) if (__builtin_constant_p (x)) ...
) в стандартных заголовках GCC (только __TBB_machine_lg
/__TBB_Log2
).