5

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)

  • 4
    Your compiler **even warned you**. It's UB. – Hatted Rooster Oct 25 '16 at 12:27
  • 2
    Your image of text [isn't very helpful](//meta.unix.stackexchange.com/q/4086). It can't be copied into an editor, and it doesn't index very well, meaning that other users with the same problem are less likely to find the answer here. Please [edit] your post to incorporate the relevant text directly (preferably using copy+paste to avoid transcription errors). – Toby Speight Oct 25 '16 at 12:33
  • Yes, it warned me indeed. But the point is the second operand is still 32 in both cases. It's undefined behaviour, I get it, but why the undefined behaviour is different in this 2 cases? @TobySpeight you have the source code in text. The image is there to prove this happens and to let people see the compiler version and how I compiled it – caution2toxic Oct 25 '16 at 12:33
  • You should be able to copy/paste the compiler warnings and version information directly into your question. – Toby Speight Oct 25 '16 at 12:36
  • As bit-shifting is only defined for up to 31 bits for a 32 bit variable, either the compiler or the processor (maybe interesting to check the assembler here) seem to only evaluate the lowest 5 bits of the shift value, resulting in a shift modulo 32. E.g. when shifting by 33, the result is 0x7fffffff, and for 63 it's 1. – Karsten Koop Oct 25 '16 at 12:43

5 Answers5

6

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.

2501
  • 25,460
  • 4
  • 47
  • 87
  • But still, why the undefined behaviour differs? If it would give 0xffffffff in both cases I would be fine with that (not an intuitively true result, but still, it would not differ). It's like doing the same thing 2 times and get different result... – caution2toxic Oct 25 '16 at 12:42
  • 4
    @caution2toxic That's what makes it undefined. The compiler is free to do as it wishes. – dbush Oct 25 '16 at 12:46
  • 1
    Undefined behaviour is undefined. There's no requirement for it to even be deterministic. – Toby Speight Oct 25 '16 at 13:42
4

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".

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
2

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.

Louis Person
  • 41
  • 1
  • 4
0

If you are targetting an environment with 32-bit int, then shifting by 32 or more bits is Undefined Behaviour:

if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined.

It is your responsibility as a programmer to avoid invoking UB.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
-1

It depends on the compiler. But in your example, in the first case, x is signed, so the shift is arithmetic (the left-most bit is re-inserted from the left). In the second case, the literal 32 is treated as unsigned, so the shift becomes logical (0 is inserted from the left). This explains the ffffffff 0 result.

AhmadWabbi
  • 2,253
  • 1
  • 20
  • 35