简体   繁体   中英

Behavior of bitwise shift operators when right operand is negative

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.

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

  • The result of a << -3 is then 2^29 * a ; for a = 3 , this is 1610612736 .
  • The result of 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).

For C#

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 is int or uint , the shift count is given by the low-order 5 bits of count . In other words, the shift count is computed from count & 0x1F
  • When the type of x is long or ulong , the shift count is given by the low-order 6 bits of count . In other words, the shift count is computed from count & 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM