Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C strange behaviour when bit-shifting [duplicate]

Why the following code, compiled with gcc, prints "ffffffff 0" instead of "0 0"? The bits are shifted to the right by 32 positions in both instructions. It doesn't make much sense, since x == 32, but still this strange result happens...

#include <stdio.h>

int main(void)
{
    int x = 32;
    printf("%x\n", 0xffffffff >> x);
    printf("%x\n", 0xffffffff >> 32);
    return 0;
}

Edit: enter image description here

Edit2: Yes, the compiler warned me. But this is not the point. I am using 0xffffffff as a mask that I bitshift with a variable. For example, when I bitshift with 8 I want 0xffffff (and it does that). When I bitshift with 31 I want 0x1 as a result (and it does that). And when I bitshift with 32 it gives me 0xffffffff (instead of 0, which is the beahaviour when I have 32 as a literal, not a variable). It is strange and for my purpose it is really unconvenient to make a special case for 32 since it should give 0 (and it does, but only when 32 is a literal)

like image 782
caution2toxic Avatar asked Oct 25 '16 12:10

caution2toxic


People also ask

What happens when you shift bits?

Logical bit shifting may be useful for multiplying or dividing unsigned integers by powers of two. For example, if the value "0001" or "1" is shifted left, it becomes "0010" or "2," shifted to the left again it becomes "0100," or "4." Shifting to the right has an opposite effect of dividing the value by two per shift.

What arithmetic operation is performed when you shift right 2 bits in binary?

Shifting left by n bits on a signed or unsigned binary number has the effect of multiplying it by 2n. Shifting right by n bits on a two's complement signed binary number has the effect of dividing it by 2n, but it always rounds down (towards negative infinity).

What is arithmetic shift explain with example?

Arithmetic right shift means shifting the bits to the right and MSB(most significant bit) is same as in the original number. Example: Arithmetic right shift of number 1 0 1 1 0 1 0 1 is 1 1 0 1 1 0 1 0.

What is arithmetic right shift used for?

2. Arithmetic : This micro-operation shifts a signed binary number to the left or to the right position. In an arithmetic shift-left, it multiplies a signed binary number by 2 and In an arithmetic shift-right, it divides the number by 2.


3 Answers

Assuming int and unsigned int are 32 bit wide, the integer constant 0xffffffff is of type unsigned int.

Right shifting an integer with a value of greater or equal the width of the integer will result in undefined behavior1.

This happens in both cases in your example.

Update:

Undefined behavior means that anything can happen. Getting the value 0xffffffff instead of 0 fits this behavior.

You cannot shift by the width of the integer type, because it says so in the standard. You must make a check if the value is greater or equal the width of the type your working with.


1 (Quoted from: ISO/IEC 9899:201x 6.5.7 Bitwise shift operators 3)
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

like image 70
2501 Avatar answered Nov 14 '22 14:11

2501


Edit2: Yes, the compiler warned me. But this is not the point. I am using 0xffffffff as a mask that I bitshift with a variable. For example, when I bitshift with 8 I want 0xffffff (and it does that). When I bitshift with 31 I want 0x1 as a result (and it does that). And when I bitshift with 32 it gives me 0xffffffff (instead of 0, which is the beahaviour when I have 32 as a literal, not a variable). It is strange and for my purpose it is really strange to make a special case for 32 since it should give 0 (and it does, but only when 32 is a literal)

Yes it is the point, very much so. The moment you try to shift it by 32 which is, as your compiler said, >= width you invoke undefined behaviour. All bets are off at this point, no assumptions can be made anymore as to what the program will do. It's free to not print out anything or format your drive. So it isn't "strange".

like image 41
Hatted Rooster Avatar answered Nov 14 '22 13:11

Hatted Rooster


In addition to the undefined behaviours that the other answers are talking about, the difference in result comes from GCC optmizing the second bitshift.

If you disassemble the code using the -S flag, you can notice that only one shrl instruction is used:

subq    $16, %rsp
movl    $32, -4(%rbp)
movl    -4(%rbp), %eax
movl    $-1, %edx
movl    %eax, %ecx
shrl    %cl, %edx
movl    %edx, %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
movl    $0, %eax

The compiler saw that it could compute the second bitshift at compile time as will never change and replaced the computation by its result, here 0.

like image 2
Louis Person Avatar answered Nov 14 '22 15:11

Louis Person