Главная chevron_right Структура chevron_right Побитовые операторы chevron_right >> (bitshift right)

>> (bitshift right)

Оператор >> сдвигает биты значения вправо на заданное число позиций — удобен для деления на степени двойки и извлечения отдельных байт.

Полная статья

Описание

Оператор побитового сдвига вправо >> перемещает все биты левого операнда на указанное количество позиций в сторону младших разрядов. Биты, которые «выходят» за правый край, теряются безвозвратно.

Сдвиг вправо на n позиций эквивалентен целочисленному делению на 2ⁿ — это быстрая операция, которую компилятор часто использует сам, но вы можете применять её явно, когда нужна скорость или работа с отдельными битами.

Синтаксис

variable >> number_of_bits;

Параметры

  • variable: значение, биты которого нужно сдвинуть. Допустимые типы данных: любой целочисленный тип — byte, short, int, long, unsigned short и другие.
  • number_of_bits: количество позиций для сдвига — положительное число, строго меньшее разрядности типа variable. Допустимые типы данных: int.

Пример кода

    int a = 40;     // двоичное: 0000000000101000
    int b = a >> 3; // двоичное: 0000000000000101, десятичное: 5

Примечания и предупреждения

Что происходит с крайними битами

Когда выражение x >> y сдвигает значение x вправо на y бит, крайние правые биты выходят за пределы числа и отбрасываются — восстановить их уже нельзя.

То, чем заполняются освободившиеся левые биты, зависит от знаковости типа:

  • Если x имеет беззнаковый тип (например, unsigned int), освободившиеся левые биты заполняются нулями. Это логический сдвиг вправо — предсказуемый и простой.
  • Если x имеет знаковый тип (например, int), освободившиеся биты заполняются копией знакового бита (самого левого). Такое поведение называется арифметическим сдвигом или расширением знака — оно гарантирует, что знак результата совпадает со знаком x.
    int x = -16;          // двоичное: 1111111111110000
    int y = 3;
    int result = x >> y;  // двоичное: 1111111111111110, десятичное: -2

Расширение знака — не всегда желаемое поведение. Если вам нужно, чтобы слева всегда вставлялись нули, приведите переменную к беззнаковому типу перед сдвигом:

    int x = -16;                        // двоичное: 1111111111110000
    int y = 3;
    int result = (unsigned int) x >> y;  // двоичное: 0001111111111110, десятичное: 8190

Сдвиг вправо как деление на степень двойки

Благодаря расширению знака оператор >> корректно делит на степени двойки даже для отрицательных чисел:

    int x = -1000;
    int y = x >> 3; // целочисленное деление -1000 на 8, результат y = -125

Однако помните, что при работе с отрицательными числами результат округляется в сторону минус бесконечности (то есть «вниз»), а не к нулю, как при обычном делении:

    int x = -1001;
    int y = x >> 3; // деление сдвигом всегда округляет вниз, результат y = -126
    int z = x / 8;  // оператор деления округляет к нулю, результат z = -125

Где применяется

  • Извлечение отдельных байт из многобайтового значения — например, чтобы получить старший байт int, достаточно написать x >> 8.
  • Быстрое целочисленное деление — сдвиг на 1 делит на 2, на 2 — на 4 и так далее, без операции деления.
  • Разбор битовых полей — при чтении данных из регистров периферии или пакетов протоколов вроде I²C и SPI.
  • Масштабирование показаний АЦП — перевод 10-битного значения analogRead() в 8-битный диапазон одним сдвигом >> 2.

Практические замечания

  • Сдвигать на количество бит, равное или превышающее разрядность типа, — неопределённое поведение в C++. Для int (16 или 32 бит в зависимости от платы) number_of_bits должен быть строго меньше этого значения.
  • Если вы работаете с byte или unsigned int и ожидаете нули слева — всё будет именно так. Проблемы с расширением знака возникают только со знаковыми типами.
  • Чтобы избежать неожиданного расширения знака, объявляйте переменные явно как unsigned, если в них хранятся битовые маски или сырые данные.
  • Результат сдвига вправо знакового отрицательного числа зависит от компилятора (стандарт C++ допускает оба варианта), но на Arduino (AVR, ARM) расширение знака работает стабильно.
  • При отладке удобно выводить промежуточные значения через Serial.println(x, BIN) — так сразу видно, какие биты куда сдвинулись.

Полезные материалы