为什么在2的补码(-1>> 1)== -1而不是0?

时间:2016-05-23 16:54:40

标签: c#

请注意我认为一些评论者误解了我的问题,即我不理解整数除法和浮点除法 - 更多说明:我期望-1/2 == -1 >> 1 == 0,但事实上-1 >> 1 = -1。< /强>

我学习了两个补充。我理解在两个补码的上下文中关于位移的特殊之处在于右移需要保持位的符号,这样右移一个负数应该填充1而不是0。移位应始终填充0.这在Wikipedia's article中解释。

根据文章,这背后的动机是保持位移操作的等效性和相应的乘法或除法2.然而,我立即注意到的一个特例是-1。根据上述规则,-1>>1不等于-1/2

我的问题是我应该如何理解这一点?在优化乘法和除法中应用位移时应该采取哪些预防措施?

这里有一个C#(应该与其他语言相同)代码说明我的意思:

class Program
{
    static void Main(string[] args)
    {
        foreach (int x in new[] { 0, 1, 2, 3, -1, -2, -3 })
        {
            int a = x >> 1;
            int b = x / 2;
            Console.WriteLine($"Number:{x}, x>>1: {a}, x/2: {b}");
        }
    }
}

这会产生以下输出:

Number:0, x>>1: 0, x/2: 0
Number:1, x>>1: 0, x/2: 0
Number:2, x>>1: 1, x/2: 1
Number:3, x>>1: 1, x/2: 1
Number:-1, x>>1: -1, x/2: 0
Number:-2, x>>1: -1, x/2: -1
Number:-3, x>>1: -2, x/2: -1

5 个答案:

答案 0 :(得分:2)

如果您尝试进行除法,则不应在代码中使用shift操作。优化器能够比您更好地解决这个问题。当然,如果你真的知道自己在做什么,并且你完全使用无符号整数,并且你正在编写汇编,请继续。在那之前,只需使用普通的数学运算符进行正常的数学运算。

如果你问为什么-1 >> 1 == -1,这很容易。负值看起来像二进制中的所有值,或十六进制中的0xFFFFFFFF。将这些位向右移动,然后将新的1移入左侧的空洞,你就可以得到你开始的了!

答案 1 :(得分:0)

结果仍然一致。右移1的影响除以2 向下舍入

在奇数正值的情况下,“额外”0.5被丢弃。在奇数负值的情况下,它会反过来。

在上面给出的例子中,-1的一半是-0.5,向下舍入到-1。同样,-3的一半是-1.5,向下舍入到-2。

答案 2 :(得分:0)

为什么-1 >> 1 == -1

每当您换班时,机器必须填写缺失值。我们将使用四位来保持简单。向左移动时,必须替换的位是尾随位:

0101 << 1  // equivalent of 5 * 2
101_  // This is filled in with a zero
1010  // 5 * 2 == 10

右移时,必须更换前导位。但由于前导位确定了有符号2s补码的符号,我们不希望丢失该符号(向左移位,或者除以2的幂,不应该使负数变为正数,反之亦然)。所以替换值是前导(符号)位已经是:

0111 >> 1  // equivalent of 7 intdiv 2
_011  // Signed, so this is filled in with a zero
0011  // 7 intdiv 2 == 3

1111 >> 1  // equivalent of -1 intdiv 2, kinda
_111  // Signed, so this is filled in with a 1
1111  // -1 intdiv 2 == -1

但是,如果这是无符号表示,则只需用零填充前导位:

1111 >> 1  // equivalent of 15 intdiv 2
_111  // Unsigned, so this is filled in with a 0
0111  // 15 intdiv 2 == 7

进一步阅读:https://msdn.microsoft.com/en-us/library/336xbhcz.aspx

答案 3 :(得分:0)

c的Wiki文章有误。这句话:These rules preserve the common semantics that left shifts multiply the number by two and right shifts divide the number by two.在查看奇数负整数时对c不正确。

对于整数除法c使用round towards zero(或truncation toward zero,如标准中所述),即如果实际结果为1.5,则整数结果将为{ {1}}。如果实际结果为1,则整数结果为-1.5

对于-1,标准没有规定在进行右移时应该对负整数执行什么 - 它是实现定义的。

如果您的系统使用两个补码,则右移不会与除法相同。通常,移位与舍入无关。但是,如果你想看一个两个补码的右移作为一个除法,你必须注意到它向负无穷大方向转。即:如果实际结果为c,则整数结果为1.5。如果实际结果为1,则整数结果为-1.5

结论:对于奇数负整数,Rigth shift not 与除以2相同。

您的直接问题:

-2

如果您认为右移为2除法,则实际结果为-1 >> 1 is ?? ,但由于它的工作方式类似于负无穷大,因此结果将四舍五入为-0.5

但如上所述 - 右移除以2.你必须看一下比特级别。

在位级别,仅仅是因为-1的位模式没有改变。 在两个补码中-1是一个-1位模式,例如

all-ones

当您右移两个补码整数为-1时,您只需得到相同的位模式,因此得到相同的值。你在LSB移出int a = -1; // Pattern 11111111.11111111.11111111.11111111 并在MSB移位1。所以二进制模式保持不变。

那么1会发生什么:

-3

答案 4 :(得分:-1)

考虑这种情况:

-1 = 0b11

-1&gt;&gt; 1 =&gt; 0b11&gt;&gt; 1 =&gt; 0b11(左边多了1个)(= -1)

如果你回头看-3&gt;&gt; 1个案例(在程序员模式下准备计算机的计算) 你应该看到0b111101&gt;&gt; 1变为0b111110(-2)