Проблема 64-битного сдвига
Почему этот код не записывает 0 как последний элемент, а 18446744073709551615? (скомпилировано с g++)
#include <iostream>
using namespace std;
int main(){
unsigned long long x = (unsigned long long) (-1);
for(int i=0; i <= 64; i++)
cout << i << " " << (x >> i) << endl;
cout << (x >> 64) << endl;
return 0;
}
9 ответов
Когда вы сдвигаете значение на большее количество бит, чем размер слова, оно обычно сдвигается на mod word-size
, По сути, сдвиг на 64 означает сдвиг на 0 битов, что равно не сдвигу вообще. Вы не должны полагаться на это, поскольку это не определено стандартом и может отличаться на разных архитектурах.
Сдвиг числа на число битов, равное или превышающее его ширину, является неопределенным поведением. Вы можете только безопасно сдвинуть 64-битное целое число между 0 и 63 позициями.
Это предупреждение от компилятора должно быть подсказкой:
"предупреждение: счетчик правого смещения>= ширина типа"
Это приводит к неопределенному поведению:
http://sourcefrog.net/weblog/software/languages/C/bitshift.html
Вы переполняете смену. Если вы заметили, GCC даже предупреждает вас:
предупреждение: счетчик смещения вправо>= ширина типа
Как так? Вы включаете 64 в качестве допустимого сдвига, который является неопределенным поведением. от 0 до 64 - 65 номеров (в том числе 0). 0 - первый бит (очень похоже на массивы).
#include <iostream>
using namespace std;
int main(){
unsigned long long x = (unsigned long long) (-1);
for(int i=0; i < 64; i++)
cout << i << " " << (x >> i) << endl;
cout << (x >> 63) << endl;
return 0;
}
Будет производить результат, который вы ожидаете.
Ты можешь использовать:
static inline pack_t lshift_fix64(pack_t shiftee, short_idx_t shifter){
return (shiftee << shifter) & (-(shifter < 64));
}
за такой трюк, (-(shifter < 64)) == 0xffff ffff ffff ffff
если сдвиг < 64 и (-(shifter < 64)) == 0x0
иначе.
Я получил:
test.c:8: warning: right shift count >= width of type
так что, возможно, это неопределенное поведение?
Ну, вы меняете слишком много раз. Вы переходите от 0 до 64 включительно, что в общей сложности 65 раз. Вы обычно хотите:
for(int i=0; i < 64; i++)
....
Еще одна ловушка для неосторожных: я знаю, что это старая ветка, но я пришел сюда в поисках помощи. Я был пойман на 64-битной машине, используя 1<< k, когда я имел в виду 1L << k; никакой помощи от компилятора в этом случае:(
Битовая комбинация -1 выглядит как 0xFFFFFFFFFFFFFFFF в шестнадцатеричном формате для 64-битных типов. Таким образом, если вы напечатаете ее как переменную без знака, вы увидите наибольшее значение, которое может содержать 64-разрядная переменная без знака, т.е. 18446744073709551615.
При сдвиге битов нам все равно, что означает значение в этом случае, то есть не имеет значения, если переменная подписана или не подписана, она обрабатывается одинаково (в этом случае все биты сдвигаются на один шаг вправо).