Операция циклического (rotary/circular) сдвига — это смещение битов числа на определённое количество разрядов с переносом «выпавших» бит в противоположный конец. В языке C нет встроенного оператора для этой задачи; такое решение исторически связано с переносимостью стандартной библиотеки и необходимостью явно определять поведение для разных платформ.
Проблема состоит в том, что стандартные операторы сдвига (<<, >>) не делают сдвиг циклическим: они лишь сдвигают биты, а «выпавшие» заменяют нулями. Для циклического сдвига нужно явно скомбинировать результаты двух сдвигов и маскировать результат заданным количеством бит.
Решение — реализовать циклический сдвиг вручную. Для 32-разрядного unsigned числа это выглядит так:
uint32_t rotate_left(uint32_t value, unsigned int shift) { return (value << shift) | (value >> (32 - shift)); } uint32_t rotate_right(uint32_t value, unsigned int shift) { return (value >> shift) | (value << (32 - shift)); }
Ключевые особенности:
Может ли оператор << или >> реализовать циклический сдвиг без дополнительных операций?
Нет. Обычные сдвиги заменяют вышедшие за пределы разряды нулями, а не переносят их на другую сторону.
Что произойдёт, если сделать сдвиг на размер типа (shift равен количеству бит в числе)?
Поведение не определено (Undefined Behavior по стандарту C), нужно обязательно делать сдвиг по модулю размера типа.
Для signed типов безопасно делать rotary shift этими функциями?
Нет, всегда используйте unsigned типы, так как побитовые сдвиги для signed типов ведут себя по-разному в зависимости от компилятора и архитектуры.
Использование обычного сдвига для циклического смещения:
uint32_t x = 0xFA3C0F00; uint32_t y = x << 5; // Не носит циклический характер
Плюсы:
Минусы:
Использование ручной функции rotary shift с учётом размера типа и защиты от UB:
uint32_t x = 0xFA3C0F00; uint32_t y = rotate_left(x, 5);
Плюсы:
Минусы: