The ISO C99 standard has this little snippet dealing with the bitshift operators:
shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
The integer promotions are performed on each of the operands. The type of the result is
that of the promoted left operand. 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.
In other words, once you start trying to shift a 64-bit (in your implementation) unsigned long
by 64 bits or more, all bets are off. It can return 0, the original value, or 42. It can even format your hard drive and mail porn to your boss (though I've never seen an implementation actually do this).
You may find that some implementations (or even the underlying hardware of the implementation) will optimise these instructions by effectively only using the relevant bits. In other words, for a 64-bit value, it may well only use the least significant seven bits (effectively and
-ing it with 0x3f
) to give you an operand of 0 through 63.
In that case, since 64 & 0x3f
is equal to zero, it's effectively a no-op.
In any case, why it's acting the way it is is supposition. Undefined behaviour is something you really should avoid. If you really want the behaviour to work as you expect, change:
unsigned long const b = a >> shift_amount;
into something like:
unsigned long const b = (shift_amount < 64) ? a >> shift_amount : 0;
as seen in this code (64-bit values are ULLs on my system):
#include <stdio.h>
#include <limits.h>
void rightshift_test(int const shift_amount) {
unsigned long long a = 0xabcd1234abcd1234ULL;
printf("a: %llx, shift_amount:%d: ", a, shift_amount);
unsigned long long const b = (shift_amount < 64) ? a >> shift_amount : 0;
printf("b: %llx\n", b);
}
int main() {
rightshift_test(56);
rightshift_test(60);
rightshift_test(61);
rightshift_test(62);
rightshift_test(63);
rightshift_test(64);
rightshift_test(99);
return 0;
}
The output of this is:
a: abcd1234abcd1234, shift_amount:56: b: ab
a: abcd1234abcd1234, shift_amount:60: b: a
a: abcd1234abcd1234, shift_amount:61: b: 5
a: abcd1234abcd1234, shift_amount:62: b: 2
a: abcd1234abcd1234, shift_amount:63: b: 1
a: abcd1234abcd1234, shift_amount:64: b: 0
a: abcd1234abcd1234, shift_amount:99: b: 0