2015-05-23 3 views
1

Я пытаюсь четко понять поведение операторов сдвига (особенно для граничных случаев), поэтому я разработал небольшой тест, написанный на C++.Нужна помощь - побитовый оператор с левым сдвигом

int a = odd_value; //321 in my case but it should not matter (the last bit to be 1) 
    print(0, a); 

    a = a << 31; // (this gives a segmentation fault, as normal because it tries the sign bit becomes 1 but all the other bits are 0). 
    print(0, a); //the segmentation fault happens here - it prints the minimum integer value and then gives the fault 
    a = (a << 1) + 1; // but if then I do this, shouldn't it set a to -1 ?? 
    print(a); //gives 0 

    void print(int stackCallIndex, int nb) 
    { 
    if(nb) 
    { 
     print(++stackCallIndex, nb >> 1); 
     if(nb & 1) printf("%d", 1); 
     else  printf("%d", 0); 

     if(stackCallIndex % 8 == 0) printf(" "); 
    } 
    } 
+4

Сегментация неисправности? – Barry

+0

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

+3

Вы не должны получать ошибку сегментации от печати 'int' O.o –

ответ

3

В соответствии с вашим кодом я бы поставил на stackoverflow из-за бесконечной рекурсии, если вы попытаетесь напечатать отрицательные значения.

void print(int stackCallIndex, int nb) 
    { 
    if(number) 
    { 
     print(++stackCallIndex, nb >> 1); // likely an infinite recursion here. 
     if(nb & 1) printf("%d", 1); 
     else  printf("%d", 0); 

     if(stackCallIndex % 8 == 0) printf(" "); 
    } 
    } 

так зачем же это бесконечная рекурсия? Строго в соответствии со стандартными значениями смещения отрицательных целых чисел с прямым сдвигом определяется реализация.

В большинстве реализаций будет делать arithmetic right shift, а это означает, что 1111 1100 (-4 в дополнительном 8bit) сдвигается вправо на 1 приведет к 1111 1110 (-2 в дополнительном 8bit), как вы можете видеть, что вы всегда заполнить бит знака снова, чтобы ваш номер никогда не доходил до 0, а условие if всегда верно.


Вообще делать манипуляции с битами подписанных значений является плохой идеей, они предполагают осуществление/неопределенное поведение в некоторых случаях. Лучше всего использовать значения без знака перед использованием манипуляции с битами.

+0

Значит, он никогда не перейдет к 0, потому что бит знака всегда сохраняется? –

+0

@SlowTrout Да. Попробуйте изменить параметр с 'int nb' на' unsigned nb', и он должен работать. –

+0

Он делает. Большое спасибо. Не могли бы вы указать мне на звуковую документацию относительно этих операторов? –

3

Если вы хотите поведение стандарт, то это не определено. Согласно [expr.shift]:

Значение E1 << E2 является E1 сдвинуты влево E2 битовые позиции; освобожденные биты заполняются нулями. Если E1 имеет неподписанный тип , [...]. В противном случае, если E1 имеет подписанный тип и неотрицательное значение, и E1 × 2E2 представима в соответствующем без знака типа типа результата, то значение, преобразуется к типу результата, является в результате чего значение; в противном случае поведение не определено.

Любой (нечетное число> 1) х 2 не представима uint32_t, так что поведение не определено. Ваш компилятор, по-видимому, решает реализовать это как ошибку сегментации, которая совершенно соответствует поведению (edit: er, по крайней мере, это было бы, если бы это было там, где это происходит).

Более типичным подходом было бы просто позволить бит «упасть» с конца. То есть a << 31 для нечетного числа станет 0x80000000. Но даже в этом случае другой сдвиг влево в 1 приведет к 0, поэтому вам нужно будет вычесть 1, чтобы получить -1, а не добавить 1.

+0

Строго согласно стандарту, да. Сомневаюсь, что любой компилятор действительно делает это, хотя и я предполагаю, что для бесконечной рекурсии с OP не добавлен код. –

+0

Это имеет смысл. Можете ли вы указать мне на документацию, которую вы только что опубликовали. В Интернете есть тоны сообщений, но никто не полностью объясняет граничные случаи. –

+0

@SlowTrout Ссылка на стандарт C++, в данном случае конкретная версия N4140. – Barry

1

Когда я пытаюсь, я получаю -214783648, который является наименьшим известным значением в целых числах ... это означает, что вы делаете целое число больше допустимого диапазона, поэтому в вашем случае возникает ошибка сегментации ...

+0

Проблема заключается в функции печати - видимо, она переходит в бесконечную петлю. –

+0

Позвольте мне проверить это, и я дам вам знать –

+0

ваша функция имеет ошибку ... номер должен быть nb ... кроме того, ему нужны два аргумента, как вы передаете ему только один аргумент? –

Смежные вопросы