Операции AND, OR, XOR и NOT осуществляют соответственно операторы &, |, ^ и ~. Они выполняют те же действия, что и эквивалентные им логические операторы, &&, ||, . Различие состоит в том, что побитовые операции выполняются над отдельными разрядами числа. В приведенной ниже таблице 7 показан результат выполнения каждого оператора над отдельными битами.
Таблица 1.7 – Побитовые операции
A | B | A&B | A|B | A^B | ~A |
Как правило, побитовая операция AND используется для сброса отдельных разрядов. Любой бит, значение которого равно нулю, хотя бы в одном из двух операндов будет равен нулю и в результирующем значении. Например:
1 1 0 1 0 0 1 1
& 1 0 1 0 1 0 1 0
____________
1 0 0 0 0 0 1 0
Использование оператора &.
В Unicode и ASCII коды букв нижнего Регистра отличаются от кодов букв верхнего регистра на 32. Таким образом, Для того, чтобы преобразовать символы нижнего регистра в верхний регистр, достаточно сбросить шестой бит.
Поскольку на символьный тип данных отводится 16 бит, то нужно в 16-битов числе в двоичном представлении сбросить 6-й бит. Для этого нужно число 1111111111011111 преобразовать в 10-ю систему счисления (65503) и использовать в операции AND.
Напишем программу перевода буквы нижнего регистра в верхний регистр путем сброса шестого бита кода символа.
Листинг 1.6
//Перевод букв в верхний регистр
class UpCase {
public static void main(String[] args) {
char ch;
for (int i = 0; i < 10; i++) {
ch = (char) ('a' + i);
System.out.print(ch);
//Данное выражение сбрасывает шестой бит
ch = (char) ((int) ch& 65503);
// Теперь в ch содержится
// код символа верхнего
// регистра
System.out.print(ch + " ");
}
}
}
Оператор AND удобен также в том случае, когда вам надо определить, установлен ли или сброшен некоторый бит числа. Например, приведенное ниже выражение определяет, установлен ли четвертый бит в переменной status.
if(status & 8) System.out.println(" bit 4 is on");
В данном случае число 8 использовано потому, что в его двоичном представлении (1000) установлен только четвертый бит. Таким образом, в выражении if значение true будет получено только тогда, когда четвертый бит значения, содержащегося в переменной status, также установлен. Подобный подход можно применить для преобразования значения типа byte в двоичный формат.
Листинг 1.7
importjava.util.Scanner;
//Отображение битов в составе байта
publicclassShowBits {
public static void main(String[] args) {
byte val = 0;
Scanner sc = new Scanner(System.in);
System.out.println("Введите число типа byte:12 ");
if (sc.hasNextByte()) {
val = sc.nextByte();
}
for (int t = 128; t > 0; t = t / 2) {
if ((val & t) != 0) {
System.out.print("1 ");
} else {
System.out.print("0 ");
}
}
}
}
В цикле for последовательно проверяется каждый бит переменной val; для чтобы определить, установлен ли бит, используется операция AND. Если бит установлен, отображается цифра 1, в противном случае выводится цифра 0.
Побитовая операция OR выполняет действия, противоположные операции AND и используется для установки битов. Любой бит, значение которого равно хотя бы в одном из двух операндов, будет равен единице и в результирующем значении. Оператор OR можно использовать для преобразования символов верхнего регистра в нижний регистр.
Задание:
Напишите программу для преобразования символов верхнего регистра в нижний регистр.
Операция XOR дает результат, в котором конкретный бит установлен в том и только в том случае, когда соответствующие биты в двух операндах имеют разные значения. Ниже приведен пример выполнения операции XOR.
Операция XOR имеет одну интересную особенность, которая позволяет очень просто кодировать сообщения. Если выполнить данную операцию над некоторыми числами X и Y, a затем снова выполнить операцию XOR над результирующим, значением и числом Y, то мы снова получим число X. Таким образом, при выполнении приведенных ниже двух команд R2 получит то же значение, которое имеет x
R1 = X ^ Y;
R2 = R1 ^ Y;
Данную особенность можно использовать для создания простейшей шифрующей программы, в которой некоторое целое число будет выполнять роль ключа, применяемого как при шифровании, так и дешифровке. Над всеми символами сообщения и данным числом будет выполняться операция XOR. В первый раз данная операция будет применена при шифровании, генерируя кодированный текст. Для декодирования операция XOR выполняется во второй раз, и результатом является исходный текст. Ниже приведен код простой программы, выполняющей шифрование и дешифровку коротких сообщений.
Листинг 1. 8
// Использование операции XOR для шифрования и дешифровки сообщений class Encode {
class Encode {
public static void main(String args[]) {
String msg = "This is a test";
String encmsg = "";
String decmsg = "";
int key = 88;
System.out.print("Original message: ");
System.out.println(msg);
// Кодирование сообщения
for (int i = 0; i < msg.length(); i++) {
encmsg = encmsg + (char) (msg.charAt(i) ^ key);
}
System.out.print("Encoded message: ");
System.out.println(encmsg);
// Декодирование сообщения
for (int i = 0; i < msg.length(); i++) {
decmsg = decmsg + (char) (encmsg.charAt(i) ^ key);
}
System.out.print("Decoded message: ");
System.out.println(decmsg);
}
}
Как видите, результат применения двух операций XOR с использованием одного и того же ключа дает в результате декодированное сообщение, совпадающее с исходным.
Операция дополнения (NOT) инвертирует все биты операнда. Например, если некоторая целочисленная переменная А содержит значение, двоичное представление которого равно 10010110, результатом выполнения выражения ~A будет набор битов 01101001.
Операторы сдвига
В языке Java предусмотрена возможность сдвига битов, составляющих число влево или вправо на заданное количество позиций. Программисту доступны перечисленных ниже оператора сдвига.
Таблица 1.8 – Операторы сдвига
<< | Сдвиг влево |
>> | Сдвиг вправо |
>>> | Беззнаковый сдвиг вправо |
Формат записи этих операторов приведен ниже.
значение << число_позиций
значение >> число_позиций
значение >>> число_позиций
Здесь слева от оператора задается значение, подлежащее сдвигу, а справа – число позиций, на которые производится сдвиг.
При сдвиге влево освободившиеся младшие биты заполняются нулями. При сдвиге вправо все происходит несколько сложнее. Как вы знаете, признаком отрицательного целого числа является единица в старшем бите, поэтому при обычном сдвиге вправо старший (знаковый) разряд дублируется. Если число положительное, то в него записывается нуль, если отрицательное – единица.
Помимо сохранения знакового разряда, необходимо помнить еще об одной особенности. Отрицательные числа в языке Java (как, впрочем, и в других языках) представляются как дополнение до двух. Чтобы преобразовать положительное число в отрицательное, его надо инвертировать, а к полученному значению прибавить единицу. Таким образом, значение -1 представляется двоичной последовательностью 11111111. Сдвинув это значение вправо на любое число позиций, мы снова получим -1!
Если вы не хотите, чтобы при сдвиге вправо сохранялся знаковый разряд, можете использовать беззнаковый сдвиг вправо (оператор >>>), при этом освободившиеся старшие разряды всегда будут заполняться нулями. Беззнаковый сдвиг удобно использовать для обработки значений, не являющихся числами, например кодов состояния.
При любом сдвиге биты теряются. Циклический сдвиг в языке Java не поддерживается, и нет возможности восстановить потерянные разряды.
Ниже приведен код программы, которая демонстрирует эффект от использования операторов сдвига. Целочисленное значение 1 представляется набором битов, в которых лишь младший равен 1. К этому значению восемь раз применяется операция сдвига влево. После каждого сдвига на экран выводится восемь младших разрядов числа. Затем единица устанавливается в восьмой позиции и производятся сдвиги вправо.
Выполняя сдвиг над значениями byte и short, необходимо соблюдать осторожность, поскольку исполняющая система Java автоматически преобразует в тип int, а лишь затем выполняет необходимые вычисления. Например, вы сдвинете вправо значение типа byte, то оно будет сначала преобразовано в int. Результат сдвига будет также иметь тип int. Обычно такое преобразование не влечет за собой никаких последствий. Однако если вы попытаетесь сдвинуть отрицательное значение, то при преобразовании в тип int старшие биты будут заполнены единицами. Это оправдано при обычном сдвиге вправо, но при беззнаковом сдвиге в старшем разряде будет обнаружена неожиданная единица. Лишь после двадцати четырех сдвигов появятся нулевые значения.
Варианты записи побитовых операций
Для всех побитовых операций предусмотрена сокращенная запись, подобная сокращенной записи логических и арифметических операций. Например, приведенные ниже две строки кода эквиваленты. В каждой из них выполняется операция XOR над значением переменной x и числом 127.
x = x ^ 127;
x ^= 127;
Задачи
1. В переменной n хранится двузначное число. Создайте программу, вычисляющую и выводящую на экран сумму цифр n.
2. В переменной n хранится трёхзначное число. Создайте программу, вычисляющую и выводящую на экран сумму цифр n.
3. В переменной n хранится вещественное число с ненулевой дробной частью. Создайте программу, округляющую число n до ближайшего целого и выводящую результат на экран.
4. В переменных q и w хранятся два натуральных числа. Создайте программу, выводящую на экран результат деления q на w с остатком. Пример вывода программы (для случая, когда в q хранится 21, а в w хранится 8):
21/8=2и5востатке