Материал предоставлен http://it.rfet.ru

Операторы сдвига

В C# имеется возможность сдвигать двоичные разряды, составляющие целое значение, влево или вправо на заданную величину. Для этой цели в C# определены два приведенных ниже оператора сдвига двоичных разрядов.

<< Сдвиг влево

>> Сдвиг вправо

Ниже приведена общая форма для этих операторов:

значение << число_битов
значение >> число_битов

где число_битов — это число двоичных разрядов, на которое сдвигается указанное значение.

При сдвиге влево все двоичные разряды в указываемом значении сдвигаются на одну позицию влево, а младший разряд сбрасывается в нуль. При сдвиге вправо все двоичные разряды в указываемом значении сдвигаются на одну позицию вправо. Если вправо сдвигается целое значение без знака, то старший разряд сбрасывается в нуль. А если вправо сдвигается целое значение со знаком, то разряд знака сохраняется. Напомним, что для представления отрицательных чисел старший разряд целого числа устанавливается в 1. Так, если сдвигаемое значение является отрицательным, то при каждом сдвиге вправо старший разряд числа устанавливается в 1. А если сдвигаемое значение является положительным, то при каждом сдвиге вправо старший разряд числа сбрасывается в нуль.

При сдвиге влево и вправо крайние двоичные разряды теряются. Восстановить потерянные при сдвиге двоичные разряды нельзя, поскольку сдвиг в данном случае не является циклическим.

Ниже приведен пример программы, наглядно демонстрирующий действие сдвига влево и вправо. В данном примере сначала задается первоначальное целое значение, равное 1. Это означает, что младший разряд этого значения установлен. Затем это целое значение сдвигается восемь раз подряд влево. После каждого сдвига выводятся восемь младших двоичных разрядов данного значения. Далее процесс повторяется, но на этот раз 1 устанавливается на позиции восьмого разряда, а по существу, задается целое значение 128, которое затем сдвигается восемь раз подряд вправо.

// Продемонстрировать применение операторов сдвига.

using System;

class ShiftDemo {
  static void Main() {
    int val = 1;

    for (int i = 0; i < 8; i++) {
      for (int t = 128; t > 0; t = t / 2) {
        if ((val & t) != 0)
          Console.Write("1 ");
        if ((val & t) == 0)
          Console.Write("0 ");
      }
      Console.WriteLine();
      val = val << 1;  // сдвиг влево
    }
    Console.WriteLine();

    val = 128;
    for (int i = 0; i < 8; i++) {
      for (int t = 128; t > 0; t = t / 2) {
        if ((val & t) != 0)
          Console.Write("1 ");
        if ((val & t) == 0)
          Console.Write("0 ");
      }
      Console.WriteLine();
      val = val >> 1;  // сдвиг вправо
    }
  }
}

Результат выполнения этой программы выглядит следующим образом.

0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0
0 0 0 1 0 0 0 0
0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0
1 0 0 0 0 0 0 0

1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 1

Двоичные разряды соответствуют форме представления чисел в степени 2, и поэтому операторы сдвига могут быть использованы для умножения или деления целых значений на 2. Так, при сдвиге вправо целое значение удваивается, а при сдвиге влево — уменьшается наполовину. Разумеется, все это справедливо лишь в том случае, если крайние разряды не теряются при сдвиге в ту или иную сторону. Ниже приведен соответствующий пример.

// Применить операторы сдвига для умножения и деления на 2.

using System;

class MultDiv {
  static void Main() {
    int n;

    n = 10;

    Console.WriteLine("Значение переменной n: " + n);

    // Умножить на 2.
    n = n << 1;
    Console.WriteLine("Значение переменной n после " +
                      "операции n = n * 2:  " + n);

    // Умножить на 4.
    n = n << 2;
    Console.WriteLine("Значение переменной n после " +
                      "операции n = n * 4:  " + n);

    // Разделить на 2.
    n = n >> 1;
    Console.WriteLine("Значение переменной n после " +
                      "операции n = n / 2:   " + n);

    // Разделить на 4.
    n = n >> 2;
    Console.WriteLine("Значение переменной n после " +
                      "операции n = n / 4:  " + n);
    Console.WriteLine();

    // Установить переменную n в исходное состояние,
    n = 10;
    Console.WriteLine("Значение переменной n: " + n);

    // Умножить на 2 тридцать раз.
    n = n << 30;  // данные теряются

    Console.WriteLine("Значение переменной п после " +
                      "сдвига на 30 позиций влево: " + n);
  }
}

Ниже приведен результат выполнения этой программы.

Значение переменной n: 10
Значение переменной n после операции n = n * 2:  20
Значение переменной n после операции n = n * 4:  80
Значение переменной n после операции n = n / 2:   40
Значение переменной n после операции n = n / 4:  10

Значение переменной n: 10
Значение переменной п после сдвига на 30 позиций влево: -2147483648

Обратите внимание на последнюю строку приведенного выше результата. Когда целое значение 10 сдвигается влево тридцать раз подряд, информация теряется, поскольку двоичные разряды сдвигаются за пределы представления чисел для типа int. В данном случае получается совершенно «непригодное» значение, которое оказывается к тому же отрицательным, поскольку в результате сдвига в старшем разряде, используемом в качестве знакового, оказывается 1, а следовательно, данное числовое значение должно интерпретироваться как отрицательное. Этот пример наглядно показывает, что применять операторы сдвига для умножения или деления на 2 следует очень аккуратно. (Подробнее о типах данных со знаком и без знака см. в главе Типы данных, литералы и переменные.)

Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕПоразрядные составные операторы присваивания