43

基本上是标题中的问题。我正在查看 MVC 2 源代码:

[Flags]
public enum HttpVerbs {
    Get = 1 << 0,
    Post = 1 << 1,
    Put = 1 << 2,
    Delete = 1 << 3,
    Head = 1 << 4
}

我只是好奇双左尖括号的<<作用。

4

15 回答 15

130

当你写

1 << n

您将位组合向左移动000000001n从而将n其放入 2 的指数中:

2^n

所以

1 << 10

真的是

1024

对于说 5 个项目的列表,您for将循环 32 次。

于 2014-01-29T10:42:25.943 回答
82

它被称为left-shift运算符。看看文档

左移运算符使第一个操作数中的位模式向左移动第二个操作数指定的位数。移位操作腾出的位被零填充。这是一个逻辑移位,而不是移位和旋转操作。

演示运算符的简单示例left-shift

for (int i = 0; i < 10; i++)
{
    var shiftedValue = 1 << i;
    Console.WriteLine(" 1 << {0} = {1} \t Binary: {2}",i,shiftedValue,Convert.ToString(shiftedValue,2).PadLeft(10,'0'));
}

//Output:

// 1 << 0 = 1      Binary: 0000000001
// 1 << 1 = 2      Binary: 0000000010
// 1 << 2 = 4      Binary: 0000000100
// 1 << 3 = 8      Binary: 0000001000
// 1 << 4 = 16     Binary: 0000010000
// 1 << 5 = 32     Binary: 0000100000
// 1 << 6 = 64     Binary: 0001000000
// 1 << 7 = 128    Binary: 0010000000
// 1 << 8 = 256    Binary: 0100000000
// 1 << 9 = 512    Binary: 1000000000

向左移动一位相当于乘以 2。事实上,移动位比标准乘法更快。让我们看一个证明这一事实的示例:

假设我们有两种方法:

static void ShiftBits(long number,int count)
{
    long value = number;
    for (int i = 0; i < count; i+=128)
    {
          for (int j = 1; j < 65; j++)
          {
              value = value << j;
          }
          for (int j = 1; j < 65; j++)
          {
               value = value >> j;
          }
    }
}

static void MultipleAndDivide(long number, int count)
{
      long value = number;
      for (int i = 0; i < count; i += 128)
      {
            for (int j = 1; j < 65; j++)
            {
                value = value * (2 * j);
            }
            for (int j = 1; j < 65; j++)
            {
                value = value / (2 * j);
            }
      }
}

我们想像这样测试它们:

ShiftBits(1, 10000000);
ShiftBits(1, 100000000);
ShiftBits(1, 1000000000);
...
MultipleAndDivide(1, 10000000);
MultipleAndDivide(1, 100000000);
MultipleAndDivide(1, 1000000000);
...

结果如下:

Bit manipulation 10.000.000 times: 58 milliseconds
Bit manipulation 100.000.000 times: 375 milliseconds
Bit manipulation 1.000.000.000 times: 4073 milliseconds

Multiplication and Division 10.000.000 times: 81 milliseconds
Multiplication and Division 100.000.000 times: 824 milliseconds
Multiplication and Division 1.000.000.000 times: 8224 milliseconds
于 2014-01-29T10:41:36.360 回答
63

那将是按位左移运算符。

对于每个左移,该值实际上乘以 2。因此,例如,写入value << 3会将值乘以 8。

它在内部真正做的是将值的所有实际位移到一个位置。因此,如果您有值 12(十进制),则二进制为00001100; 将其向左移动一个位置会将其变为00011000, 或 24。

于 2010-03-22T15:33:42.467 回答
57

它是按位左移,它通过将数字的二进制等效数字按给定(右侧)数字移动来工作。

所以:

temp = 14 << 2

二进制等价于 1400001110将其移动 2 次意味着从右侧推零并将每个数字向左侧移动,使其00111000等于 56。

视觉的

在您的示例中:

i < (1 << list.Count)
  • 0000000001 = 1 如果list.Count = 0结果是 0000000001 = 1
  • 0000000001 = 1 如果list.Count = 1结果是 0000000010 = 2
  • 0000000001 = 1 如果list.Count = 2结果是 0000000100 = 4
  • 0000000001 = 1 如果list.Count = 3结果是 0000001000 = 8

等等。通常它是相等2 ^ list.Count的(2 的 list.Count 次方)

于 2014-01-29T11:42:44.193 回答
36

那是左移位运算符。它将左操作数的位模式向左移动右操作数中指定的二进制位数。

Get = 1 << 0, // 1
Post = 1 << 1, // 2
Put = 1 << 2,  // 4
Delete = 1 << 3, // 8
Head = 1 << 4  // 16

这在语义上等价于lOperand * Math.Pow(2, rOperand)

于 2010-03-22T15:33:30.590 回答
23

循环的目的最有可能生成或操作列表中项目集的所有子集。并且循环体很可能也有一个好的位(har har)位操作,即另一个左移和位与。(所以重写它以使用 Pow 将是非常愚蠢的,我简直不敢相信有这么多人实际上建议这样做。)

于 2014-01-29T13:48:47.390 回答
15

那是位移。它基本上只是通过在右侧添加 0 来将位移动到左侧。

public enum HttpVerbs {
    Get = 1 << 0,    // 00000001 -> 00000001 = 1
    Post = 1 << 1,   // 00000001 -> 00000010 = 2
    Put = 1 << 2,    // 00000001 -> 00000100 = 4
    Delete = 1 << 3, // 00000001 -> 00001000 = 8
    Head = 1 << 4    // 00000001 -> 00010000 = 16
}

更多信息请访问http://www.blackwasp.co.uk/CSharpShiftOperators.aspx

于 2010-03-22T15:34:13.977 回答
12

除了 Selman22 的回答,还有一些例子:

我将列出一些值list.Count以及循环是什么:

list.Count == 0: for (int i = 0; i < 1; i++)
list.Count == 1: for (int i = 0; i < 2; i++)
list.Count == 2: for (int i = 0; i < 4; i++)
list.Count == 3: for (int i = 0; i < 8; i++)

等等。

于 2014-01-29T10:44:23.983 回答
9

“位左移。” 1 << 0意思是“取整数值 1 并将其位左移零位。” 即,00000001保持不变。 1 << 1表示“取整数值 1 并将其位左移一位。” 00000001变成00000010.

于 2010-03-22T15:35:31.760 回答
8

它的 (<<) 是按位左移运算符,它移动二进制对象的位值。左操作数指定要移位的值,右操作数指定值中的位要移位的位置数。

在您的情况下,如果 list.count 的值为 4,则循环将运行直到 i < (1<< 4) 即16 (00010000)

00000001 << 4 = 00010000(16)

于 2014-01-29T12:54:14.193 回答
7

该表达式在 c# 中(1 << N)使用位移位。

在这种情况下,它用于执行 2^N 的快速整数求值,其中 n 为 0 到 30。

一个很好的工具年轻的鞭打者不了解位移如何工作的开发人员是程序员模式下的 Windows Calc,它可以可视化位移对各种大小的有符号数的影响。和函数分别等于Lsh和。Rsh<<>>

在循环条件内使用 Math.Pow 进行评估(在我的系统上)比 N = 10 的问题代码慢大约 7 倍,这是否重要取决于上下文。

将“循环计数”缓存在单独的变量中会稍微加快速度,因为不需要在每次迭代时重新评估涉及列表长度的表达式。

于 2014-01-29T18:29:11.833 回答
7

它暗示在许多答案中,但从未直接说明......

对于将二进制数左移的每个位置,都将数字的原始值加倍。

例如,

十进制 5 二进制左移一位是十进制 10,或十进制 5 加倍。

十进制 5 二进制左移 3 是十进制 40,或十进制 5 加倍 3 倍。

于 2014-01-29T20:19:41.810 回答
6

以前的答案已经解释它的作用,但似乎没有人猜测为什么。在我看来,这段代码的原因很可能是循环正在迭代列表成员的每个可能组合——这是我能明白你为什么要迭代最多 2^{list.数数}。因此,该变量i将被错误命名:而不是索引(我通常将 'i' 解释为含义),它的位表示列表中项目的组合,因此(例如)如果位可以选择第一项设置为零i(i & (1 << 0)) != 0),如果设置了第 1 位,则设置第二项((i & (1 << 1)) != 0),依此类推。1 << list.Count因此,它是第一个不对应于列表中项目的有效组合的整数,因为它表示选择了不存在的list[list.Count].

于 2014-01-29T18:53:22.590 回答
5

我知道这个答案已经解决了,但我认为可视化可能会对某人有所帮助。

[Fact] public void Bit_shift_left()
{
    Assert.Equal(Convert.ToInt32("0001", 2), 1 << 0); // 1
    Assert.Equal(Convert.ToInt32("0010", 2), 1 << 1); // 2
    Assert.Equal(Convert.ToInt32("0100", 2), 1 << 2); // 4
    Assert.Equal(Convert.ToInt32("1000", 2), 1 << 3); // 8
}
于 2010-03-22T15:44:16.833 回答
0

<<是左位移运算符。如果你有数字 3,即二进制的 00000011,你可以写成3 << 200001100,或者十进制的 12。

于 2020-11-02T11:00:13.010 回答