右操作数为负时位移位操作符的行为

本文关键字:位操作 操作符 操作数 | 更新日期: 2023-09-27 18:12:36

#include <iostream>
int main()
{
    int s(9);
    std::cout<<(s<<-3);
}

g++给出如下警告:

[Warning] left shift count is negative [enabled by default]

MSVS 2010给出以下警告:

warning c4293: '<<' : shift count negative or too big, undefined behavior

现在我很好奇Java和c#中发生了什么?

I've try 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);
    }
}

项目结果:

1610612736
0

c#的转变:

namespace left_shift_nagative
{
    class Program
    {
        static void Main(string[] args)
        {
            int s = 3;
            Console.WriteLine(s << -3);
            Console.WriteLine(s >> -3); 
        }
    }
}
输出:

1610612736
0

输出1610612736是怎么来的?这里发生了什么?Java语言规范(JLS)和c#语言规范或标准对此有何规定?如何& lt; & lt;和>>运算符在Java &c#吗?当使用右移时,我是如何得到输出0的?我真的很困惑。

右操作数为负时位移位操作符的行为

我将回答Java部分(不能说c#,但可能是一样的)。

移位运算符>><<在JLS第15.19节中定义。引用(强调我的):

如果左操作数的提升类型为int,则只使用右操作数的最低五位作为移位距离。这就好像右操作数受到了位逻辑与操作符&(§15.22.1),掩码值为0x1f (0b11111)。因此,实际使用的移位距离始终在0到31的范围内,包括。

因此,当你通过-3进行移位时,就像你通过-3 & 0x1f进行移位一样,也就是29(只使用了右操作数的最低五位)。

  • a << -3的结果是2^29 * a;对于a = 3,这是>>0。
  • a >> -3的结果是floor(a / 2^29);对于a = 3,这是0

请注意,当左操作数的提升类型为long而不是int时,使用的掩码值为0x3f(仅使用右操作数的六位最低位)。

对于c#

来自c#规范章节7.9 -

对于预定义的运算符,要移位的位数按如下方式计算:

  • x的类型为int<<0时,移位计数由count的低阶5位给出。换句话说,移位计数是从count & 0x1F
  • 计算的。
  • x的类型为longulong时,移位计数由count的低阶6位给出。换句话说,移位计数是从count & 0x3F
  • 计算的。

这意味着移位的符号被忽略。事实上,正如Jon Skeet在注释规范中指出的那样,由于规范定义的位屏蔽,以下代码将在i == 31之后包装。

for (int i = 0; i < 40; i++)
{
    Console.WriteLine(int.MaxValue >> i);
}