与<<;操作人员

本文关键字:lt 操作 | 更新日期: 2023-09-27 18:26:56

在处理C#移位运算符时,我遇到了左移位运算符的意外行为
然后我尝试了这个简单的功能:

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) 移位运算符是旋转运算符,因此结果应该是:
2a)0x10 << -3 = 00000002(与1a相同)
2b)0x10 << -6 = 40000000

但显示的结果既不符合1),也不符合2)!!!

与<<;操作人员

负数是两个补码,所以-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是一样的,但现在使用& 63而不是& 31-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

其中CCD_ 18中的部分是位于CCD_ 19"左侧"且不存在的部分。

它与负数的表示有关。-1对应于所有的1,因此它的五个最低有效位和为31,并且0x10左移31位得到所有的0(根据文档,已经设置的高阶位被丢弃)。

越来越大的负数对应于移位30、29等位。已经在0x10中设置的比特处于基于零的位置4,因此为了不丢弃它,移位必须最多为31-4=27比特,这在i == -5时发生。

如果你尝试Console.WriteLine((-1).ToString("x8")):,你可以很容易地看到发生了什么

ffffffff

更新:当第一个操作数是long时,您会看到类似的行为,因为现在从第二个操作数开始计算六个最低有效位:0x10L << -1向左移动63位,等等。

左移位运算符不会将第二个负操作数视为右移。它将只使用该值的低五位,并使用该值进行左移。

-10xFFFFFFFF)的低五位将是310x0000001F),因此第一操作数0x10向左移位31步,在结果的最高有效位中只留下最低有效位。

换句话说,0x10 << -10x10 << 31相同,后者将是0x800000000,但结果仅为32位,因此它将被截断为0x00000000

使用长值时,第二个操作数将使用六个最低有效位。值-1变为63,并且比特仍然被移出长的范围之外。