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:
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)
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.
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).
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.
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.
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.
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".
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With