与<<;操作人员
本文关键字: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 == 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
是一样的,但现在使用& 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位,等等。
左移位运算符不会将第二个负操作数视为右移。它将只使用该值的低五位,并使用该值进行左移。
值-1
(0xFFFFFFFF
)的低五位将是31
(0x0000001F
),因此第一操作数0x10
向左移位31步,在结果的最高有效位中只留下最低有效位。
换句话说,0x10 << -1
与0x10 << 31
相同,后者将是0x800000000
,但结果仅为32位,因此它将被截断为0x00000000
。
使用长值时,第二个操作数将使用六个最低有效位。值-1
变为63,并且比特仍然被移出长的范围之外。