在处理C#移位运算符时,我遇到了shift-left运算符的意外行为 然后我尝试了这个简单的功能:
for (int i = 5; i >= -10; i--) {
int s = 0x10 << i;
Debug.WriteLine(i.ToString().PadLeft(3) + " " + s.ToString("x8"));
}
结果如下:
5 00000200
4 00000100
3 00000080
2 00000040
1 00000020
0 00000010
-1 00000000 -> 00000008 expected
-2 00000000 -> 00000004 expected
-3 00000000 -> 00000002 expected
-4 00000000 -> 00000001 expected
-5 80000000
-6 40000000
-7 20000000
-8 10000000
-9 08000000
-10 04000000
直到今天,我预计<<
运算符可以处理第二个操作数的负值
MSDN没有说明使用第二个操作数的负值时的行为。但MSDN表示运营商只使用低5位(0-31),这应该适合负值。
我还尝试使用long
值:long s = 0x10L << i;
,但结果相同。
那么这里会发生什么?
修改
正如你的答案中所述,负值表示是不的原因
我对所有情况都得到了同样错误的结果:
0x10<<-3 = 0x00000000 (wrong!)
0x10<<(int)(0xfffffffd) = 0x00000000 (wrong!)
0x10<<(0x0000001d = 0x00000000 (wrong!)
expected = 0x00000002
编辑#2
其中一个应该是真的:
1)班次操作员是实际班次 - 操作员,因此结果应为:
1a)0x10 << -3 = 00000002
1b)0x10 << -6 = 00000000
2)移位运算符是 rotate - 运算符,因此结果应为:
2a)0x10 << -3 = 00000002
(与1a相同)
2b)0x10 << -6 = 40000000
但显示的结果既不适合1)也不适合2)!!!
答案 0 :(得分:3)
负数有两个补码,因此-1 == 0xFFFFFFFF
,0xFFFFFFFF & 31 == 31
,-2 == 0xFFFFFFFE
和0xFFFFFFFE & 31 == 30
等等。
-10 == 0xFFFFFFF6, and 0xFFFFFFF6 & 31 == 22, in fact:
(0x10 << 22) == 04000000
要显示的一些代码:
const int num = 0x10;
int maxShift = 31;
for (int i = 5; i >= -10; i--)
{
int numShifted = num << i;
uint ui = (uint)i;
int uiWithMaxShift = (int)(ui & maxShift);
int numShifted2 = num << uiWithMaxShift;
Console.WriteLine("{0,3}: {1,8:x} {2,2} {3,8:x} {4,8:x} {5}",
i,
ui,
uiWithMaxShift,
numShifted,
numShifted2,
numShifted == numShifted2);
}
使用long
它是相同的,但现在而不是& 31
你有& 63
。 -1 == 63
,-2 == 62
和-10 == 54
一些示例代码:
const long num = 0x10;
int maxShift = 63;
for (int i = 5; i >= -10; i--)
{
long numShifted = num << i;
uint ui = (uint)i;
int uiWithMaxShift = (int)(ui & maxShift);
long numShifted2 = num << uiWithMaxShift;
Console.WriteLine("{0,3}: {1,8:x} {2,2} {3,16:x} {4,16:x} {5}",
i,
ui,
uiWithMaxShift,
numShifted,
numShifted2,
numShifted == numShifted2);
}
(int x) << y == (int x) << (int)(((uint)y) & 31)
(long x) << y == (long x) << (int)(((uint)y) & 63)
(int x) << y == (int x) << (Math.Abs(y) & 63)
(long x) << y == (long x) << (Math.Abs(y) & 63)
你认为“应该是”“如果它是”必须是“ecc 无关紧要那将是美好的。虽然1和0是“接近”(它们的二进制表示在不同位的数量上具有1的“距离”),但0和-1是“远”(它们的二进制表示具有32或64的“距离”)不同的位)
你认为你应该得到这个:
-1 00000000 -> 00000008 expected
-2 00000000 -> 00000004 expected
-3 00000000 -> 00000002 expected
-4 00000000 -> 00000001 expected
但实际上你没看到的是你得到了这个:
-1 (00000008) 00000000
-2 (00000004) 00000000
-3 (00000002) 00000000
-4 (00000001) 00000000
-5 (00000000) 80000000 <-- To show that "symmetry" and "order" still exist
-6 (00000000) 40000000 <-- To show that "symmetry" and "order" still exist
(...)
中的部分是int
的“左”部分,但不存在。
答案 1 :(得分:3)
它与representation of negative numbers有关。 -1
对应于全1,因此其五个最低有效位总和为31,左移0x10
乘以31位给出全零(已设置的高位被丢弃)文档)。
越来越大的负数对应于移位30,29等位。已在0x10
中设置的位在从零开始的位置4,因此为了不丢弃它,移位必须至多为31 - 4 = 27位,这发生在{{1}时}。
如果你尝试例如,你可以很容易地看到发生了什么i == -5
:
Console.WriteLine((-1).ToString("x8"))
更新:当第一个操作数为ffffffff
时,您会看到类似的行为,因为现在有六个最低有效位从第二个操作数开始计数:long
向左移位63位等
答案 2 :(得分:3)
左移运算符没有看到负第二个操作数作为右移。它只使用值的低五位并使用它进行左移。
值-1
(0xFFFFFFFF
)的低五位将为31
(0x0000001F
),因此第一个操作数0x10
将转移到留下31步,只留下结果最高位的最低有效位。
换句话说,0x10 << -1
与0x10 << 31
相同,0x800000000
,但结果只有32位,因此会被截断为0x00000000
。< / p>
当使用长值时,第二个操作数使用六个最低有效位。值-1
变为63,并且这些位仍然在长整数范围之外移出。