Прежде чем переходить к битовым операциям, необходимо уточнить, каким именно образом целые числа представляются в двоичном виде. Конечно, для неотрицательных величин это практически очевидно:
0 О
1 1
2 10
3 11
4 100
5 101
и так далее. Однако как представляются отрицательные числа? Во-первых, вводят понятие знакового бита. Первый бит начинает отвечать за знак, а именно 0 означает положительное число, 1 — отрицательное. Но не следует думать, что остальные биты остаются неизменными. Например, если рассмотреть 8-битовое представление:
Такой подход неверен! В частности, мы получаем сразу два представления нуля — 00000000 и 100000000, что нерационально. Правильный алгоритм 1йожно представить себе так. Чтобы получить значение -1, надо из 0 вычесть 1: 00000000
-00000001
-11111111
Итак, -1 в двоичном виде представляется как 11111111. Продолжаем применять тот же алгоритм (вычитаем I):
О 00000000 -1 11111111 -2 11111110 -3 11111101
и так далее до значения 10000000, которое представляет собой наибольшее по модулю отрицательное число. Для 8-битового представления наибольшее положительное число 01111111 (=127), а наименьшее отрицательное 10000000 (=-128). Поскольку всего 8 бит определяет 2*=256 значений, причем одно из них отводится для нуля, то становится ясно, почему наибольшие по модулю положительные и отрицательные значения различаются на единицу, а не совпадают.
Как известно, битовые операции "и", "или", "исключающее или" принимают два аргумента и выполняют логическое действие попарно над соответствующими битами аргументов. При этом используются те же обозначения, что и для логических операторов, но, конечно, только в первом (одиночном) варианте. Например, вычислим выражение 5&6:
00000101 // число 5 в двоичном виде & 00000110 // число 6 в двоичном виде
00000100 //проделали операцию "и" попарно над битами // в каждой позиции, то есть выражение 5&6 равно 4.
Исключение составляет лишь оператор "не" или "NOT", который для
Побитовых операций записывается как ~ (для логических было !). Этот оператор меняет каждый бит в числе на противоположный. Например, можно легко установить общее правило для получения битового представления отрицательных чисел:
Если п - целое положительное число, то -п в битовом представлении равняется-(п-1).
Наконец, осталось рассмотреть лишь операторы побитового сдвига. В Java есть один оператор сдвига влево и два варианта сдвига вправо. Такое различие связано с наличием знакового бита.
При сдвиге влево оператором « все биты числа смещаются на указанное количество позиций влево, причем освободившиеся, справа позиции заполняются нулями. Эта операция аналогична умножению на 2п и действует вполне предсказуемо, как при положительных, так и при отрицательных аргументах.
Рассмотрим примеры применения операторов сдвига для значений типа Int, т.е. 32-битных чисел. Пусть положительным аргументом будет число 20, а отрицательным -21.
Как видно из примера, неожиданности возникают тогда, когда значащие биты начинают занимать первую позицию и влиять на знак результата.
При сдвиге вправо все биты аргумента смещаются на указанное коли-Ч1^тво позиций, соответственно, вправо. Однако встает вопрос — каким значением заполнять освобождающиеся позиции слева, в том числе и отвечающую за знак. Есть два варианта. Оператор » использует для заполнения этих позиций значение знакового бита, то есть результат всегда имеет же знак, что и начальное значение. Второй оператор >» заполняет их полями, то есть результат всегда положительный.
// Сдвиг вправо для положительного числа 20 // Оператор »
Очевидно, что для положительного аргумента операторы » и >» 1{{работают совершенно одинаково. Дальнейший сдвиг на большее количевo позиций будет также давать нулевой результат.
Как видно из примеров, эти операции аналогичны делению на 2п. Причем, если для положительных аргументов с ростом п результат закономерно стремится к 0, то для отрицательных предельным значением является -1.