Проблема 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.

При сдвиге битов нам все равно, что означает значение в этом случае, то есть не имеет значения, если переменная подписана или не подписана, она обрабатывается одинаково (в этом случае все биты сдвигаются на один шаг вправо).

Другие вопросы по тегам