右操作数为负时位移运算符的行为
Behavior of bitwise shift operators when right operand is negative
在 C 和 C++ 中,如果使用 >>
和 <<
(右移和左移运算符)时右操作数为负,程序的行为是不确定的。
考虑以下程序:
#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# 中发生了什么?
我试过以下程序
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# 语言规范或标准对此有何评论?当 Java 和 C# 中给出负移位计数时,<< 和 >> 运算符如何工作?使用右移时如何得到输出 0?我真的很困惑。
我会回答 Java 部分(不能代表 C#,但可能是一样的)。
移位运算符 >>
和 <<
在 JLS section 15.19 中定义。引用(强调我的):
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.
因此,当你移动 -3
时,就好像你移动 -3 & 0x1f
一样,即 29(只有右手操作数的最低五位被使用)。
- 那么
a << -3
的结果就是2^29 * a
;对于 a = 3
,这是 1610612736
。
- 那么
a >> -3
的结果就是floor(a / 2^29)
;对于 a = 3
,这是 0
。
注意,当左操作数的提升类型是long
,而不是int
时,使用的掩码值是0x3f
(只有6个最低位使用右侧的操作数)。
对于 C#
来自 C# 规范第 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
这意味着忽略移位的符号。事实上,正如 Jon Skeet 在注释规范中指出的那样,由于规范定义的位掩码,以下代码将在 i == 31
之后换行。
for (int i = 0; i < 40; i++)
{
Console.WriteLine(int.MaxValue >> i);
}
在 C 和 C++ 中,如果使用 >>
和 <<
(右移和左移运算符)时右操作数为负,程序的行为是不确定的。
考虑以下程序:
#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# 中发生了什么?
我试过以下程序
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# 语言规范或标准对此有何评论?当 Java 和 C# 中给出负移位计数时,<< 和 >> 运算符如何工作?使用右移时如何得到输出 0?我真的很困惑。
我会回答 Java 部分(不能代表 C#,但可能是一样的)。
移位运算符 >>
和 <<
在 JLS section 15.19 中定义。引用(强调我的):
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.
因此,当你移动 -3
时,就好像你移动 -3 & 0x1f
一样,即 29(只有右手操作数的最低五位被使用)。
- 那么
a << -3
的结果就是2^29 * a
;对于a = 3
,这是1610612736
。 - 那么
a >> -3
的结果就是floor(a / 2^29)
;对于a = 3
,这是0
。
注意,当左操作数的提升类型是long
,而不是int
时,使用的掩码值是0x3f
(只有6个最低位使用右侧的操作数)。
对于 C#
来自 C# 规范第 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
这意味着忽略移位的符号。事实上,正如 Jon Skeet 在注释规范中指出的那样,由于规范定义的位掩码,以下代码将在 i == 31
之后换行。
for (int i = 0; i < 40; i++)
{
Console.WriteLine(int.MaxValue >> i);
}