- Битовые операции — AND (&), OR (|), XOR (^), NOT (~) и сдвиги <<, >> — работают напрямую с двоичным представлением числа и выполняются за одну машинную инструкцию, поэтому в разы быстрее обычной арифметики.
- AND и OR используют для битовых масок и флагов, XOR — для простого шифрования и переключения бита, NOT — для инверсии, сдвиги — для быстрого умножения и деления на степени двойки.
- Логика одинакова в C++, JavaScript, Python и PHP — отличаются символы части операторов и обработка знаковых чисел.
- Ниже — таблицы истинности, код на нескольких языках, практическое применение и типичные ошибки: например, путаница & с && или использование >> вместо >>> для беззнакового сдвига в JS.
Битовые операции имеют множество применений. Криптография, компьютерная графика, хэш-функции, цифровая обработка изображений, алгоритмы сжатия и сетевые протоколы — вот лишь некоторые примеры, где такие операции чрезвычайно полезны.
В блоге студии web-разработки YuSMP Group рассмотрели, как работают базовые битовые операторы AND, OR, XOR, NOT и сдвиги, и собрали несколько приемов по манипулированию битами. Такие низкоуровневые оптимизации наша команда использует и в промышленной разработке ПО под ключ — от бэкенда до мобильных и embedded-модулей.
Что такое битовые операции и зачем они нужны
Бит — наименьшая единица данных: он принимает только одно из двух значений, 0 или 1. Битовая операция работает не с числом целиком, а с каждым его битом по отдельности, обрабатывая двоичное представление числа напрямую. В отличие от логических операторов && и ||, которые сравнивают булевы значения true/false и используют короткое замыкание, битовые операторы &, |, ^, ~, <<, >> применяются одновременно ко всем битам числа и возвращают число, а не булево значение.
Именно поэтому битовые операции выполняются за одну-две машинные инструкции процессора и в разы быстрее обычной арифметики или условных конструкций — это классический приём оптимизации в системном программировании, компьютерной графике, играх, сетевых протоколах и алгоритмах сжатия.
Основные битовые операторы: AND, OR, XOR, NOT
К основным битовым операторам относят три парные логические операции и одну унарную — инверсию. Каждая работает по фиксированному правилу, которое проще всего показать таблицей истинности: на входе — значения двух битов (0 или 1), на выходе — результат операции.
И (AND, &)
| a | b | a & b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
Оператор И (в большинстве языков — &) возвращает 1 только тогда, когда оба бита равны 1 — во всех остальных случаях результат 0. Пример на JavaScript:
На практике AND используют, чтобы «обнулить» ненужные биты числа — например, проверить, включён ли конкретный флаг: flags & MASK.
ИЛИ (OR, |)
| a | b | a | b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
Оператор ИЛИ возвращает 1, если хотя бы один из битов равен 1. Его используют, чтобы включить (установить) бит, не трогая остальные: flags = flags | MASK. Пример на Python:
Исключающее ИЛИ (XOR, ^)
| a | b | a ^ b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
XOR возвращает 1, если биты различаются, и 0 — если совпадают. Два свойства XOR делают его особенно полезным: x ^ x = 0 (число, сложенное само с собой по XOR, даёт ноль) и x ^ 0 = x (XOR с нулём не меняет число). На этом строится обмен значений двух переменных без временной переменной и простое симметричное шифрование (см. раздел «Где применяют» ниже). Пример на C++:
НЕ (NOT, ~)
Унарный оператор NOT (~) инвертирует каждый бит числа: 0 становится 1, а 1 — 0. Важная деталь: в языках со знаковыми целыми числами (C++, Java, JavaScript) результат ~x равен -x - 1, потому что отрицательные числа хранятся в дополнительном коде — это частая причина ошибок у новичков, которые ожидают увидеть просто «перевёрнутые биты» в десятичном виде. Например, ~5 в 32-битном int даёт -6, а не положительное число с перевёрнутыми битами.
Побитовые сдвиги (<<, >>, >>>)
Операторы сдвига перемещают все биты числа влево или вправо на заданное количество позиций:
- << (сдвиг влево) — дописывает нули справа; каждый сдвиг на 1 эквивалентен умножению на 2.
- >> (арифметический сдвиг вправо) — отбрасывает младшие биты, а освобождающиеся старшие биты заполняются знаковым битом (сохраняет знак числа); сдвиг на 1 эквивалентен целочисленному делению на 2.
- >>> (логический сдвиг вправо, есть в Java и JavaScript) — заполняет освобождающиеся биты нулями независимо от знака, поэтому у отрицательных чисел даёт совсем другой результат, чем >>.
Пример на JavaScript:
let x = 5; console.log(x << 2); // 20 (5 * 4) console.log(-8 >> 1); // -4 (знак сохранён) console.log(-8 >>> 1); // 2147483644 (знак потерян)Сдвиги на степень двойки быстрее универсального умножения и деления и в компилируемых, и в интерпретируемых языках, поэтому их до сих пор используют в высоконагруженных алгоритмах и системном коде — хотя современные компиляторы часто сами заменяют x * 2 на x << 1, если это безопасно.
Где применяют битовые операции на практике
Помимо академического интереса, битовые операции решают конкретные инженерные задачи.
- Битовые маски и флаги. Вместо десятка булевых полей в объекте можно хранить набор независимых флагов в одном числе:
PERMISSION_READ = 1,PERMISSION_WRITE = 2,PERMISSION_EXEC = 4. Проверка — черезpermissions & PERMISSION_WRITE, включение —permissions |= PERMISSION_WRITE, выключение —permissions &= ~PERMISSION_WRITE. Такой подход экономит память и применяется в правах доступа, конфигурациях и низкоуровневых API операционных систем. - Простое XOR-шифрование. Если зашифровать сообщение операцией XOR с ключом, повторное применение того же XOR с тем же ключом полностью восстанавливает исходные данные — следствие свойства
x ^ x = 0. Промышленным шифрованием такой метод не является (ключ легко подобрать при известном открытом тексте), но он лежит в основе более сложных потоковых шифров и часто встречается в учебных задачах и лёгкой обфускации данных. - Быстрая проверка чётности числа.
n & 1мгновенно определяет чётность числа (0 — чётное, 1 — нечётное), не прибегая к операции деления с остаткомn % 2. - Упаковка и распаковка цвета RGB. Цвет пикселя часто хранят одним 32-битным числом: каждый канал (R, G, B, alpha) занимает свои 8 бит. Чтобы получить канал, число сдвигают и накладывают маску:
red = (color >> 16) & 0xFF. Это применяется в графических библиотеках, обработке изображений и играх. - Оптимизация в сетевых протоколах и сжатии. Заголовки протоколов (например, IP, TCP) и форматы сжатия данных плотно упаковывают служебную информацию в отдельные биты байта, и разбор такого заголовка — это всегда набор сдвигов и масок.
Частые ошибки при работе с битовыми операциями
- Путают & и &&.
&— побитовый оператор, работает со всеми битами числа;&&— логическое И, работает с булевыми значениями и использует короткое замыкание.if (a & b)иif (a && b)в общем случае дают разный результат. - Забывают про приоритет операторов. Во многих языках
&,|и^имеют более низкий приоритет, чем операторы сравнения — выражениеflags & MASK == 0без скобок почти всегда работает не так, как ожидается, потому что сначала выполнитсяMASK == 0. - Путают >> и >>> в JavaScript и Java. Для отрицательных чисел это два разных результата: >> сохраняет знак, >>> — нет.
- Ожидают от ~x «человеческого» результата.
~xв знаковой арифметике равно-x - 1, а не положительному числу с перевёрнутыми битами. - Переполнение при сдвиге влево. Сдвиг за пределы разрядности типа (например,
1 << 32для 32-битного int) даёт неопределённое или платформозависимое поведение — частый источник трудноуловимых багов в C и C++.
Сводная таблица битовых операторов по языкам
| Операция | Python | JavaScript | C++ / Java | PHP |
|---|---|---|---|---|
| И (AND) | & | & | & | & |
| ИЛИ (OR) | | | | | | | | |
| Искл. ИЛИ (XOR) | ^ | ^ | ^ | ^ |
| НЕ (NOT) | ~ | ~ | ~ | ~ |
| Сдвиг влево | << | << | << | << |
| Сдвиг вправо (знаковый) | >> | >> | >> | >> |
| Сдвиг вправо (беззнаковый) | — (не поддерживается) | >>> | Java: >>>, в C++ нет | — (не поддерживается) |
В Python нет отдельного беззнакового сдвига и оператора >>>, потому что целые числа в Python не имеют фиксированной разрядности и хранятся с произвольной точностью.
Смежные приёмы: деление, остаток и возведение в степень
Рядом с чисто битовыми AND/OR/XOR/NOT в этом же классе оптимизаций часто разбирают смежные арифметические приёмы — деление с остатком и возведение в степень, — которые в соревновательном программировании и повседневном коде используют вместе с битовыми операциями для ускорения вычислений.
Для нахождения частного и остатка (C++)
В этом примере вы научитесь находить частное и остаток от деления С++.
#include <iostream> using namespace std; int main() { int divisor, dividend, quotient, remainder; cout << "Enter dividend: "; cin >> dividend; cout << "Enter divisor: "; cin >> divisor; quotient = dividend / divisor; remainder = dividend % divisor; cout << "Quotient = " << quotient << endl; cout << "Remainder = " << remainder; return 0; }Чтобы вычислить частное и остаток, делитель и делимое должны быть целыми числами.
Оператор вычисляет частное (между вещественными или целочисленными переменными).
Оператор модуля % вычисляет остаток при делении одного целого числа на другое (нельзя использовать для переменных с плавающей запятой).
Найти остаток от деления (js)
Оператор ( %) возвращает javascript остаток от деления и всегда принимает знак делимого.
Описание
Для операции n % d, n называется делимым и называется делителем. Операция возвращает значение NaN, если один из операндов равен NaN, n равен ± бесконечности или d равен ± 0. В противном случае, если d± бесконечность или n± 0, n возвращается.
Когда оба операнда не равны нулю и конечны, остаток r вычисляется как r := n - d * q где q целое число, r имеющее тот же знак, что и n, но максимально близкое к 0.
Примеры С положительным дивидендом 13 % 5; // 3 1 % -2; // 1 1 % 2; // 1 2 % 3; // 2 5.5 % 2; // 1.5 С отрицательным дивидендом -13 % 5; // -3 -1 % 2; // -1 -4 % 2; // -0 С NaN NaN % 2; // NaN С бесконечностью Infinity % 2; // NaN Infinity % 0; // NaN Infinity % Infinity; // NaN 2 % Infinity; // 2 0 % Infinity; // 0Целочисленное деление в JavaScript
В JavaScript есть всемогущий Math Объект, предоставляющий массу математических функций, от констант до функций. Важно отметить, что объект Math не работает с BigInt. Внутри Math Объекта находятся статические свойства и методы, обеспечивающие математические функции. Похожий объект есть и в другом языке: Math.Abs() возвращает в C# модуль числа.
Одним из статических методов является floor метод, округляющий Number Аргумент типа в меньшую сторону. Таким образом, для достижения в JS целочисленного деления мы можем выполнить типичное действие и округлить ответ (число с плавающей запятой) в меньшую сторону, используя метод, чтобы получить результат.
const y = 34; const x = 12; const quotient = Math.floor(y / x); console.log(quotient);Деление без остатка в Python
Используйте оператор floor//, чтобы произвести в Python деление без остатка.
Floor всегда возвращает целое число и похож на использование математического деления с floor() функцией, примененной к результату.
Используется floor, чтобы вернуть целое число из целочисленного деления.Результатом использования оператора является математическое деление с floor() применением к результату функции.
Нужны разработчики в команду?
Подберём инженеров нужного стека под ваш проект — быстрый старт.
Возведение в степень (**)
Оператор возведения в степень js ( **) возвращает результат возведения первого операнда в степень второго операнда.
Оператор возведения в степень право ассоциативен: a ** b ** cравен a ** (b ** c). В Python степень (оператор возведения) определяется как имеющий более высокий приоритет, то же самое происходит и в других языках (например, PHP).
В JavaScript невозможно написать неоднозначное выражение возведения в степень. Например, -2 ** 2 в Bash это 4, а в других языках (таких как Python) — -4. Это недопустимо в JavaScript, так как операция неоднозначна. Вы должны заключить обе стороны в скобки — например, as -(2 ** 2)— чтобы сделать намерение недвусмысленным.
Обратите внимание, что некоторые языки программирования используют символ вставки ^ для перемножения степеней, но JavaScript использует этот символ для побитового логического оператора XOR .
Пример Основное возведение в степень 2 ** 3; // 8 3 ** 2; // 9 3 ** 2.5; // 15.588457268119896 10 ** -1; // 0.1Частые вопросы (FAQ)
Что такое битовые операции?
Битовые операции — это операторы AND (&), OR (|), XOR (^), NOT (~) и сдвиги (<<, >>, >>>), которые работают напрямую с двоичным представлением числа, обрабатывая каждый бит отдельно. В отличие от обычной арифметики они выполняются за одну машинную инструкцию процессора, поэтому применяются для оптимизации в системном программировании, графике и сетевых протоколах.
Чем оператор & отличается от логического &&?
& — это побитовый оператор: он применяется одновременно ко всем битам числа и возвращает число. && — логический оператор: он работает с булевыми значениями true/false, использует короткое замыкание (не вычисляет правый операнд, если левый уже false) и возвращает булево значение. if (a & b) и if (a && b) почти всегда дают разный результат.
Для чего используют XOR на практике?
XOR применяют для простого симметричного шифрования (повторное XOR с тем же ключом восстанавливает исходные данные), обмена значений двух переменных без временной переменной и поиска единственного неповторяющегося элемента в массиве — всё это работает благодаря свойству x ^ x = 0.
Как битовые сдвиги ускоряют умножение и деление?
Сдвиг влево (<<) на один разряд эквивалентен умножению на 2, а сдвиг вправо (>>) — целочисленному делению на 2, потому что именно так устроена позиционная двоичная система счисления. Такая операция быстрее универсального умножения или деления, поэтому её используют в высоконагруженных алгоритмах и низкоуровневом коде.
Что такое битовая маска и зачем она нужна?
Битовая маска — это число, в котором отдельные биты обозначают независимые флаги (например, права доступа на чтение, запись, выполнение). Маска позволяет проверять, включать и выключать конкретный флаг операциями &, | и &~ без десятка отдельных булевых переменных, экономя память и упрощая код.
В чём разница между >> и >>> в JavaScript?
>> — арифметический сдвиг вправо, он сохраняет знак числа, заполняя освобождающиеся биты значением знакового бита. >>> — логический (беззнаковый) сдвиг, он всегда заполняет освобождающиеся биты нулями, поэтому для отрицательных чисел даёт совсем другой, обычно очень большой положительный, результат.
В каких языках программирования есть битовые операции?
Практически во всех: C, C++, Java, C#, JavaScript, Python, PHP, Go, Rust поддерживают операторы &, |, ^, ~ и сдвиги <<, >>. Различаются только символы отдельных операторов и поведение при работе со знаковыми числами — например, в Python нет оператора беззнакового сдвига >>>, потому что целые числа там хранятся с произвольной точностью.
Веб-услуги и разработка в YuSMP Group включает в себя большой спектр работ по программированию. Наши специалисты владеют актуальными технологиями и сильными навыками, чтобы создавать качественные продукты. Посмотрите проекты, которые мы создали, чтобы убедиться в высоком уровне команды.
Обсудим ваш проект?
Соберём команду под вашу задачу и оценим проект за 2 дня — бесплатно.




