07 апреля 2020

Побитовые операции

    Данные в компьютере представляются в виде последовательности битов. В языке Perl определены бинарные операции побитового логического сравнения целых чисел и строк: & (И), | (ИЛИ) и л (исключающее ИЛИ), а также унарная операция логического отрицания ~. Результат их вычисления зависит от того, к данным какого типа они применяются: числовым или строковым. Эти операторы различают числовые данные и строки, содержимое которых может быть преобразовано в число.
     Кроме логических операций побитового сравнения, две операции сдвигают влево («) и вправо (») биты в представлении целых чисел. Эти операторы не работают со строками.

Числовые операнды
     Если хотя бы один операнд в бинарных побитовых операциях является числом, то содержимое второго операнда также должно быть числом. Операнд, являющийся строкой символов, преобразуется в числовое значение. В случае несоответствия содержимого строки десятичному числу ее значение принимается равным о и отображается предупреждающее сообщение, если установлен соответствующий режим работы интерполятора. Все числовые операнды преобразуются к целым числам простым отбрасыванием дробной части, никакого округления не происходит.
      Чтобы понять сущность побитовых операций над числовыми данными, необходимо представлять, как хранятся в программе целые числа. При задании чисел мы можем использовать одно из трех представлений: десятичное, восьмеричное или шестнадцатеричное. Однако в компьютере числа не хранятся ни в одном из указанных представлений. Они переводятся в двоичные числа — числа с основанием 2, цифры которых и называются битами. Двоичные числа представляют собой запись чисел в позиционной системе счисления, в которой в качестве основания используется число 2. Таким образом, двоичные цифры, или биты, могут принимать значения только о или 1. Например, десятичное число ю, переведенное в двоичное, представляется в виде loio. Для обратного перевода этого числа в десятичную форму представления следует, в соответствии с правилами позиционной системы счисления, произвести следующие действия:

1 * (2**3) + 0 * (2**2) + 1 * -(2**1) + 0 * (2**0)

      Язык Perl гарантирует, что все целые числа имеют длину 32 бит, хотя на некоторых машинах они могут представляться и 64 битами. Именно с двоичными представлениями целых чисел и работают все побитовые операции, преобразуя и отрицательные, и положительные числа к целому типу данных.

      Операция & побитового логического И сравнивает каждый бит правого операнда с соответствующим битом левого операнда. Если оба сравниваемых бита имеют значение 1, то соответствующий бит результирующего значения операции устанавливается равным 1, в противном случае значением бита результата будет о. В качестве примера рассмотрим следующее выражение

45.93 & 100

Прежде всего, правый операнд преобразуется к целому 45, двоичное представление которого будет

00000000000000000000000000101101

Двоичное представление левого операнда юо будет иметь вид

00000000000000000000000001100100

Результатом побитового логического И будет следующая последовательность битов

00000000000000000000000000100100

      Она соответствует десятичному целому числу 36. Следовательно, значением выражения 45.93 & юо будет целое число 36.

Замечание
В этом примере мы специально использовали 32-битное представление целых чисел, хотя для бинарных побитовых операций лидирующие нули не имеют значения. Они существенны только при операциях побитового логического отрицания и сдвигов.
     При побитовой операции логического ИЛИ | бит результата устанавливается равным 1, если хотя бы один из сравниваемых битов равен 1. Операция побитового логического ИЛИ для тех же двух чисел

45.93 | 100

даст результат равный юэ, так как при применении побитового логического ИЛИ к операндам

00000000000000000000000000101101 (десятичное 45)

И

00000000000000000000000001100100 (десятичное 100)
дает следующую цепочку битов

00000000000000000000000001101101 (десятичное 109:

2**6+2**5+2**3+2'**2+2**1)

      Побитовое исключающее ИЛИ при сравнении битов дает значение \ тогда, когда точно один из операндов имеет значение равное 1. Следовательно, 1 ^ 1=о и о ^ о=о, в остальных случаях результат сравнения битов равен о. Поэтому для тех же чисел результатом операции будет десятичное число 73.

      Операция логического отрицания ~ является унарной и ее действие заключается в том, что при последовательном просмотре битов числа все значения о заменяются на 1, и наоборот. Результат этой операции существенно зависит от используемого количества битов для представления целых чисел. Например, на 32-разрядной машине результатом операции ~1 будет последовательность битов

11111111111111111111111111111110

представляющая десятичное число 42949б7294=2 31 +2 30 +...+2 1 , тогда как на 16-разрядной машине эта же операция даст число б534=2 31 +2 30 +...+2 1 .

      Бинарные операции побитового сдвига осуществляют сдвиг битов целого числа, заданного левым операндом, влево («) или вправо (») на количество бит, определяемых правым целочисленным операндом. При сдвиге вправо недостающие старшие биты, а при сдвиге влево младшие биты числа дополняются нулями. Биты, выходящие за разрядную сетку, пропадают. Несколько примеров операций сдвига представлено ниже:

# Битовое представление числа 22: (00000000000000000000000000010110)
22 » 2 # Результат: (00000000000000000000000000000101) = 5
22 << 2 # Результат: (00000000000000000000000001011000) = 88

      Все перечисленные операции работают и с отрицательными целыми числами, только при их использовании следует учитывать, что они хранятся в дополнительном коде. Двоичная запись неотрицательного целого числа называется прямым кодом. Обратным кодом называется запись, полученная поразрядной инверсией прямого кода. Отрицательные целые числа представляются в памяти компьютера в дополнительном коде, который получается прибавлением единицы к младшему разряду обратного кода. Например, представление числа -1 получается следующим образом:

00000000000000000000000000000001 # положительное число 1 111111111111111111111111111111Ю # обратный код числа 1 11111111111111111111111111111111 # добавляем к младшему разряду 1
# и получаем представление числа -1

Именно с этим кодом числа -i будут работать все побитовые операции, если оно будет задано в качестве операнда одной из них.

Внимание
В языке Perl, как отмечалось в гл. 3, в арифметических операциях используется представление всех чисел в виде чисел с плавающей точкой удвоенной точности. Там же говорилось, что целое число можно задавать с 15 значащими цифрами, т.е. максимальное положительное целое число может быть 999 999 999 999 999. Но это число не имеет ничего общего с представлением целых чисел в компьютере, для которых может отводиться 64, 32 или 16 битов, в зависимости от архитектуры компьютера. Во всех побитовых операциях можно предсказать результат только если операндами являются целые числа из диапазона -2 32 -1. . 2 32 -1, так как ясен алгоритм их представления. Вещественные числа, не попадающие в этот диапазон, преобразуются к целым, но алгоритм их преобразования не описан авторами языка.

Строковые операнды

     Если оба операнда являются строковыми литералами или переменными, содержащими строковые данные, то операции побитового логического сравнения сравнивают соответствующие биты кода каждого символа строки. Для кодирования символов используется таблица ASCII-кодов. Если строки разной длины, то при сравнении полагается, что строка меньшей длины содержит необходимое число недостающих символов с восьмеричным кодом \ооо. Например, результатом сравнения двух строк "++" и "з" с помощью операции | побитового логического ИЛИ будет строка ",-+". Операнды этой операции представляются следующими битовыми последовательностями (каждый символ представляется 8 битами):
00101011 00101011 # Восьмеричный код символа "+" равен 053. 00110011 # Восьмеричный код символа "3" равен 063.

      Вторая строка дополняется восемью нулевыми битами и последовательно для каждой пары соответствующих бит двух строк одинаковой длины выполняется операция логического ИЛИ. Результатом выполнения этой процедуры является битовая последовательность

00111011 00101011

      При ее интерпретации как строки символов получается последовательность двух символов с восьмеричными кодами 07 3 и 053, которые соответствуют символам ";" и "+"

      Следует отметить, что в случае двух строковых операндов, содержимое которых можно преобразовать в числовые данные, подобное преобразование не происходит, и побитовые операции логического сравнения выполняются как с обычными строковыми данными:

45.93 I 100 # Результат: число 109.
"45.93" I 100 # Результат: число 109.
45.93 I "100" # Результат: число 109.
"45.93" I "100" # Результат: строка "55>93".

      В первых трех операциях этого примера строки преобразуются в числа, а потом выполняется соответствующая операция побитового логического ИЛИ двух чисел.

      Выполнение операции побитового логического отрицания ~ для строки ничем не отличается от соответствующей операции отрицания для чисел с той лишь разницей, что применяется она к битовому представлению символов строки:

~"1" # Результат: "+".
~"ab" t Результат: "ЮЭ".