A colleague was writing a bit of image processing code in C# while
working under the assumption that a bitwise shift operation by a
negative count (i.e.
lvalue >> -2) would result in the opposite shift (
lvalue << 2
in our example). Nevermind the logic behind that assumption, while
helping doing some research I've stumbled upon what might be a
portability issue in the C# language design.
Apprently C# defines the left/right-shift operators as, for example:
int operator >> ( int x, int count );
It goes on to specify the behavioural differences between 32-bit and
64-bit code but gives no indication of what happens if you shift by a
negative value (which is possible given that count is of type int); this is left undefined. This leaves certain behavioural aspects of applications up to the VM; what probably happens is (for Intel processors anyway) that the JIT compiler generates something which looks like:
mov ecx,[count]
and ecx,0x1f
shl [eval],ecx ; or sal, if x is uint...
If count is negative, this will result in a mask of the two's complement, so for -2 this would be 11110
- or a shift-left by 30. I'm not sure what prompted Tal to make the
assumption regarding negative shifts, but the fact of the matter is
that his code compiled without warning. If the default operators were
declared with uint count,
at the very least we'd get a "possible signed-unsigned mismatch"
compiler warning. Most people would slap themselves and correct their
erroneous code.
I couldn't find any reference of this with a Google search and would
be more than interested in hearing corrections, explanations or just
opinions...
Update (September 7th, 10:16): As per Eli Ofek's advice I started a discussion thread
on the MSDN forums which already proved useful. A guy calling himself
TAG suggested that the reason why the operators are defined with signed
shift count is that unsigned types are not CLS- (common language
specification-) compliant. This could very well be the case, however I
am adamant that the language specification should reflect this; also,
the fact that the CLS does not support unsigned types is nontrivial
(and not easily found), which could potentially mean a lot of projects,
open source and commercial, are in fact nonportable because they make
use of unsigned types.