Reputation: 14458
In C & C++, if right operand is negative when using >>
and <<
(shift right & shift left operator), behavior of program is undefined.
Consider following program:
#include <iostream>
int main()
{
int s(9);
std::cout<<(s<<-3);
}
g++ gives following warning:
[Warning] left shift count is negative [enabled by default]
MSVS 2010 gives following warning:
warning c4293: '<<' : shift count negative or too big, undefined behavior
Now I am curious about what happens in Java and C#?
I've tried following program
class left_shift_nagative
{
public static void main(String args[])
{
int a=3;
System.out.println(a<<-3);
System.out.println(a>>-3);
}
}
Outcome of program:
1610612736
0
C#'s turn:
namespace left_shift_nagative
{
class Program
{
static void Main(string[] args)
{
int s = 3;
Console.WriteLine(s << -3);
Console.WriteLine(s >> -3);
}
}
}
Output:
1610612736
0
How the output 1610612736 comes? What is happening here? What the Java Language Specification (JLS) and C# Language Specification or standard says about this? How << and >> operator works when negative shift count is given in Java & C#? How I got output 0 when right shift is used? I am really getting confused.
Upvotes: 0
Views: 496
Reputation: 137329
I will answer about the Java part (can't speak for C# but it is probably the same).
The shift operators >>
and <<
are defined in JLS section 15.19. Quoting (emphasis mine):
If the promoted type of the left-hand operand is int, then only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
Therefore, when you are shifting by -3
, it is exactly as if you were shifting by -3 & 0x1f
, which is 29 (only the five lowest-order bits of the right-hand operand are used).
a << -3
is then 2^29 * a
; for a = 3
, this is 1610612736
.a >> -3
is then floor(a / 2^29)
; for a = 3
, this is 0
.Note that when the promoted type of the left-operand is long
, and not int
, the mask value used is 0x3f
(only the six lowest-order bits of the right-hand operand are used).
Upvotes: 7
Reputation: 6738
From the C# spec Section 7.9 -
For the predefined operators, the number of bits to shift is computed as follows:
- When the type of
x
isint
oruint
, the shift count is given by the low-order 5 bits ofcount
. In other words, the shift count is computed fromcount & 0x1F
- When the type of
x
islong
orulong
, the shift count is given by the low-order 6 bits ofcount
. In other words, the shift count is computed fromcount & 0x3F
This means that the sign of the shift is ignored. In fact as pointed out by Jon Skeet in the annotated spec, the following code will wrap after i == 31
due to the bit masking defined by the spec.
for (int i = 0; i < 40; i++)
{
Console.WriteLine(int.MaxValue >> i);
}
Upvotes: 1