Я только что натолкнулся на функцию, которую не понимаю, и мне было интересно, не могли бы вы мне это объяснить.
unsigned long long x(unsigned long long value, int begin, int end)
{
unsigned long long mask = (1 << (end - begin)) - 1;
return (value >> begin) & mask;
}
Спасибо
uksz
Вышеуказанная функция служит в качестве маски для извлечения диапазона битов из числа. Это можно разбить на четыре этапа.
Первый шаг:
mask = 1UL << (end - begin)
<<
логически сдвиги 1
слева end - begin
биты. Поскольку бинарный файл 1
является 000001
сдвиг на 3 будет соответствовать 001000
,
Второй шаг:
mask = mask - 1
Из предыдущего шага мы установили, что маска в этой точке будет представлять собой последовательность нулей, затем единицу, а затем end - begin
количество нулей. Вычитание 1
от такого числа приведет к end - begin
наименее значимые биты 1
со всем остальным как 0
, Вычитание 1
из нашего предыдущего примера дает 000111
,
Третий шаг:
value >> begin
Это логически сместит целевой номер (тот, из которого нам нужно извлечь биты) вправо на begin
биты. Так как мы хотим, чтобы биты в диапазоне begin to end
мы можем оставить удалить биты, прежде чем begin
,
Четвертый шаг:
(value >> begin) & mask
Взятие символьного AND с маской приведет к первому begin - end
биты смещенного числа извлекаются. Это потому что 0 & x = 0
а также 1 & x = x
,
Как указано в другом ответе Вирсавии, следует позаботиться о том, чтобы написать 1UL, чтобы гарантировать, что число, смещаемое unsigned int
, Остальное перенести int
больше битов в этом int
является неопределенным поведением. 1UL является unsigned long long int
со значением 1
,
Поведение функции не определено. Так должно быть
unsigned long long mask = (1ULL << (end - begin)) - 1;
1
является int
буквально и применяя больше левых сдвигов, чем есть биты в этом int
является неопределенным поведением. 1ULL
это неподписанный длинный длинный литерал.
Как только это будет исправлено, он надежно вернет только 0 и 1 бит в диапазоне начала и конца, и 0 везде.
Первая строка смещает влево число на количество (конец — начало) битовых сдвигов.
Вторая строка сдвигается вправо на количество (правых) битовых сдвигов.
Таким образом, в конце у вас будет маска, равная битам между «началом» и «концом».
(1) 1 in the expression implies 32 bit number on 32 bit machines.
So we need uul after 1 to make it 64 bit. Will work for MOST of the
cases.
unsigned long long mask = (1ull << (end - begin)) - 1;
(2) When begin=0, end=63, we will still see the wrong mask in case#1
The mask will come out be 0x7FFFFFFFFFFFFFFF
The following will fix that problem as well.
unsigned long long mask = ((1ull << end) - (1ull << begin)) | (1ull << end);
This will generate the mask 0xFFFFFFFFFFFFFFFF