Книга: Песни о Паскале

Глава 48 Железная логика

Глава 48

Железная логика


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

Два взгляда на компьютерные «кирпичики»

Регистры построены из триггеров – элементарных ячеек памяти, способных хранить один бит информации. В регистре может быть 8, 16, 32 или 64 триггера, что соответствует 1, 2, 4 или 8 байтам. Так видят устройство компьютера инженеры-электроники.

А программисты? Они видят то же самое, только называют иначе (рис. 108). То, что электроники именуют триггерами, программисты называют битами, а регистры нам видны как байты, слова и т.д. Так, в Паскале 8-битовый регистр соответствует типу Byte, 16-битовый – типу Word, а 32-битовый – типу Longint.

Я утверждал, что простые типы данных, такие как числа и символы, неделимы. Теперь признаюсь, что это не совсем так, поскольку в регистре процессора они представлены совокупностью битов. Процессор может работать не только с регистром в целом, но и с отдельными его битами. Иногда эту способность процессора используют и программисты, для чего Паскаль дает им надлежащие средства. Сейчас мы рассмотрим их.


Рис.108 – Устройство процессора глазами электроника и программиста

Логические операции в регистрах

Взгляните на эту, на первый взгляд бессмысленную программу.

var A, B, C : integer;
begin
      A:= 5;       B:=16;       C:= A or B;
      Writeln( C );
end.

Здесь в переменную C заносится логическая сумма двух других числовых переменных. Но ведь логические операции применяют к булевым данным, причем здесь числа? Так вспомните о регистрах, где эти числа хранятся. Ведь это массивы битов! Содержимое битов можно трактовать и как числа 0 и 1, и как логические значения FALSE и TRUE. Именно так поступает Паскаль, выполняя логические действия с числами. В данном примере логически складываются шестнадцать независимых булевых пар с получением 16 битов результата. Похоже выполняются и другие логические операции с числами.

Известно, что переменная типа BOOLEAN занимает байт целиком, но использует лишь один из восьми битов, – расточительно, не так ли? Тогда как в байте можно хранить 8 булевых значений, в целом числе – 16, а в длинном целом – 32. Но экономия – не самое главное в жизни. Логические операции с числами дают интересные возможности для шифрования данных, их используют при обработке изображений и в иных случаях.

«Ладно, – скажете, – теперь бы увидеть это наяву». Легко! Наша следующая программа исследует булевы операции с числами. Самая серьезная её часть – функция преобразования байта в строку символов, то есть в двоичное представление этого числа. В программе «P_47_1» нечто похожее выполняла функция ConvertFromNumber. Сейчас мы облегчим эту функцию, избавившись от одного параметра – основания системы счисления. К тому же теперь нам надо показать все восемь двоичных разрядов числа, включая незначащие нули. В результате этих изменений появилась на свет функция ConvertTo2, которую мы видим в программе «P_48_1».

{ P_48_1 – исследование логических операций с числами }
function ConvertTo2(aNumber : integer): string;
var n, i : integer; c : char; S : string;
begin
S:=''; { Накопитель цифр }
for i:=1 to 8 do begin
n:= aNumber mod 2;       { остаток от деления }
c:= Char(n + Ord('0')); { преобразуем в цифру }
S:= c + S;       { вставляем цифру слева }
aNumber:= aNumber div 2; { частное }
end;
ConvertTo2:= S;
end;
var A, B, C : byte; { Операнды и результат }
begin {=== Главная программа ===}
repeat
      Write('A= '); Readln(A);
      Write('B= '); Readln(B);
      C:= A or B;       { логическое сложение (объединение) }
      Writeln;
      Writeln('C= A OR B');
      Writeln('A= ',ConvertTo2(A), A:5);
      Writeln('B= ',ConvertTo2(B), B:5);
      Writeln('C= ',ConvertTo2(C), C:5);
      C:= A and B; { логическое умножение (пересечение) }
      Writeln;
      Writeln('C= A AND B');
      Writeln('A= ',ConvertTo2(A), A:5);
      Writeln('B= ',ConvertTo2(B), B:5);
      Writeln('C= ',ConvertTo2(C), C:5);
      C:= not A;       { логическое отрицание (инверсия) }
      Writeln;
      Writeln('C= NOT A');
      Writeln('A= ',ConvertTo2(A), A:5);
      Writeln('C= ',ConvertTo2(C), C:5);
until A=0;
end.

Главная программа не должна вызывать вопросов: после ввода пары чисел и выполнения логических операций с ними, на экран выводятся как исходные числа, так и результаты. Причем выводятся и в двоичной, и в десятичной системах счисления, например:

      C= A OR B
A= 00001101 13
B= 00001011 11
C= 00001111 15
      C= A AND B
A= 00001101 13
B= 00001011 11
C= 00001001 9
      C= A XOR B
A= 00001101 13
B= 00001011 11
C= 00000110 6
      C= NOT A
A= 00001101 13
C= 11110010 242

По результатам этих опытов выведены правила для логических операций (табл. 12). Логическое отрицание «НЕ» отличается от прочих тем, что применяется к одному операнду.

Табл. 12 – Правила выполнения логических операций с битами

Логическая операция Пример Правило
«ИЛИ» (сложение) 1010 OR11001110 Результат единица, если ХОТЯ БЫ ОДИН из операндов равен единице.
«И» (умножение) 1010 AND11001000 Результат единица, если ОБА операнда равны единице.
«Исключающее ИЛИ» (сравнение) 1010 XOR11000110 Результат единица, если операнды ОТЛИЧАЮТСЯ.
«НЕ» (отрицание) 1010 NOT0101 Результат единица, если операнд РАВЕН НУЛЮ.

Заменив в этих правилах единицу на TRUE, а ноль на FALSE, вы получите правила для булевых данных.

Сдвиги влево и вправо

Сдвиг – одна из тех операций обработки регистров, которые выполняют все процессоры. В Паскале тоже предусмотрены две такие операции с числами: сдвиг влево (SHL) и сдвиг вправо (SHR).

Операция левого сдвига (рис. 109) перемещает все биты слова на заданное число позиций влево, при этом младшие биты заполняются нулями, а старшие теряются, например:

N:= 3;       { 3 = 00000011 }
Writeln (N shl 1);       { 6 = 00000110 }
Writeln (N shl 2);       { 12 = 00001100 }


Рис.109 – Сдвиг байта на один разряд влево

Операция правого сдвига (рис. 110) перемещает все биты слова на заданное число позиций вправо. При этом старшие биты заполняются нулями, а младшие теряются.

N:= 3;       { 3 = 00000011 }
Writeln (N shr 1);       { 1 = 00000001 }
Writeln (N shr 2);       { 0 = 00000000 }


Рис.110 – Сдвиг байта на один разряд вправо

Совместив сдвиг с логическими операциями, можно исследовать отдельные биты слова. Перед вами булева функция TestBit, принимающая два параметра: ARG – число, в котором проверяется состояние некоторого бита, и BIT – номер этого бита. Функция возвращает TRUE, если проверяемый бит содержит единицу, и FALSE в противном случае.

function TestBit (arg: longint; bit : byte): Boolean;
begin
      TestBit := (arg and (1 shl bit)) <> 0
end;

Итоги

• Процессоры построены из триггеров. Триггер – это элемент с двумя устойчивыми состояниями, которые можно трактовать либо как булевы значения TRUE и FALSE, либо как числа 0 и 1.

• Для хранения чисел и других данных, триггеры соединены в регистры. Обычно регистр состоит из 8, 16, 32 или 64 битов.

• В Паскале есть средства для работы с регистрами – это логические операции и сдвиги. Они трактуют числа как массивы битов.

А слабо?

А) Напишите программу для исследования операций сдвига (подобную программе «P_48_1»).

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


Рис.111 – Циклический сдвиг влево

В Паскале нет операций циклического сдвига. Напишите функции для циклического сдвига слова влево и вправо. Подсказка: перед сдвигом каждого бита проверяйте состояние теряемого бита, а затем восстанавливайте его в младшем или старшем разряде.

Оглавление книги

Оглавление статьи/книги

Генерация: 1.271. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз