Назначение битов 64-битной переменной
Я немного новичок в битовых операциях. Я пытаюсь сохранить информацию в переменной int64_t следующим образом:
int64_t u = 0;
for(i=0;i<44;i++)
u |= 1 << i;
for(;i<64;i++)
u |= 0 << i;
int t = __builtin_popcountl(u);
и то, что я намеревался с этим, было сохранить 44 1 в переменной u и убедиться, что все оставшиеся позиции равны 0, поэтому "t" возвращает 44. Однако, это всегда возвращает 64. С другими переменными, например, int32, это также дает сбой. Зачем?
3 ответа
Тип выражения обычно определяется самим выражением, а не контекстом, в котором оно появляется.
Ваша переменная u
имеет тип int64_t
(Между прочим, uint64_t
было бы лучше, так как вы выполняете побитовые операции).
В этой строке:
u |= 1 << i;
поскольку 1
имеет тип int
, 1 << i
также имеет тип int
, Если, как обычно, int
32 бита, это имеет неопределенное поведение для больших значений i
,
Если вы измените эту строку на:
u |= (uint64_t)1 << i;
это должно делать то, что вы хотите.
Вы также можете изменить 1
в 1ULL
, Это дает ему тип unsigned long long
, который гарантированно должен быть не менее 64 бит, но не обязательно того же типа, что и uint64_t
,
Когда вы сдвигаете старший бит 32-разрядного целого числа и преобразуете в 64-разрядный, знаковый бит будет проходить через верхние 32 бита; который вы будете ИЛИ при установке всех 64 бит, потому что ваш литерал '1' является 32-битным целым числом со знаком по умолчанию. Сдвиг также не повлияет на старшие 32 бита, поскольку значение только 32 бита; однако преобразование в 64-разрядное будет, когда конвертируемое значение будет отрицательным.
Это можно исправить написав свой первый цикл следующим образом:
for(i=0;i<44;i++)
u |= (int64_t)1 << i;
Более того, этот цикл ничего не делает, так как ORing с 0 не изменит значение:
for(;i<64;i++)
u |= 0 << i;
- __builtin_popcountl принимает значение без знака в качестве его параметра, который не всегда является 64-разрядным целым числом. Я лично использую __builtin_popcountll, который занимает много времени. Похоже, это не так для вас
- Целые числа имеют тип int по умолчанию, и, сдвигая int на что-либо больше или равное 32 (точнее, размер int в битах), вы получаете неопределенное поведение. Правильное использование: u |= 1LL << i; Здесь LL стоит долго долго.
- Ориентация с нуля ничего не делает. Вы не можете просто установить бит на определенное значение, вы должны либо ИЛИ с маской (если вы хотите установить некоторые биты в 1 с), либо И с отрицанием маски (если вы хотите установить некоторые биты в 0), отрицание выполняется с помощью тильда (~).