<<<<<<操作者

时间:2013-08-08 12:52:13

标签: c# bit-shift

在处理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)!!!

3 个答案:

答案 0 :(得分:3)

负数有两个补码,因此-1 == 0xFFFFFFFF0xFFFFFFFF & 31 == 31-2 == 0xFFFFFFFE0xFFFFFFFE & 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)

左移运算符没有看到负第二个操作数作为右移。它只使用值的低五位并使用它进行左移。

-10xFFFFFFFF)的低五位将为310x0000001F),因此第一个操作数0x10将转移到留下31步,只留下结果最高位的最低有效位。

换句话说,0x10 << -10x10 << 31相同,0x800000000,但结果只有32位,因此会被截断为0x00000000。< / p>

当使用长值时,第二个操作数使用六个最低有效位。值-1变为63,并且这些位仍然在长整数范围之外移出。