Битовое представление данных

Спасибо читательнице КГ А. Бородиной из Минска, с одобрением отозвавшейся о статье, посвященной Булевой алгебре и устройству компьютера. Она предложила поговорить о представлении данных в памяти компьютера. С удовольствием! Пожалуйста, имейте в виду, что последующие сведения излагаются для случая архитектуры IBM PC и процессоров семейства Intel x86.

(c) Компьютерная газета

В начале следует вспомнить кое-что из арифметики. Запись числа представляет его разложение по степеням числа 10, а для записи разрядов используется 10 цифр: от 0 до 9. Припомнив это, легко понять двоичную и шестнадцатеричную системы счисления, то есть формы записи чисел, отличные от десятичной.

Они нужны потому, что гораздо удобнее десятичной... для системных программистов и хакеров. В двоичной системе число разлагается по степеням двойки, а разряды записываются при помощи 0 и 1. В шестнадцатеричной системе число разлагается по степеням 16, а разряды записываются при помощи цифр от 0 до 9 и от A до F, символизирующих числа от 10 до 15.

Шестнадцатеричная система хороша для просмотра содержимого памяти, а двоичная для описания формата представления данных в памяти. Вот как выглядит типичный дамп памяти:

Приведенный дамп представляет собою 16-колоночный построчный вывод содержимого памяти в порядке возрастания адресов. Минимальной адресуемой единицей памяти является байт. Вот вы и видите последовательность байтов, значение каждого из которых представлено двумя шестнадцатеричными цифрами. Иными словами, байт - это двузначное (двухразрядное) шестнадцатеричное число.

Сами по себе числа, равно как и байты, не имеют никакого смысла до тех пор, пока мы им этот смысл не припишем. Если считать, что каждый из байтов дампа представляет целое положительное число, то первые три байта означают числа 205, 32 и 255. Здесь все просто: переводим шестнадцатеричное число в десятичное - и готово. А может быть, это символы? Тогда те же три байта будут означать букву "Н", знак пробела и букву "я" соответственно. Не всегда, конечно, а в случае кодовой страницы 1251 Windows 95/NT. Для MS DOS и/или для других кодовых страниц буквы будут иными, не говоря уже о кодировке UNICODE, где один символ представляется не одним, а двумя байтами сразу.

Чтобы уметь интерпретировать байты как целые числа со знаком, следует кое-что знать. Поскольку в процессоре нет аппаратной реализации команды вычитания целых чисел, то на деле числа складываются, причем вычитаемое предварительно преобразуется к виду "дополнения до двух". Результат, естественно, оказывается верным, как при "настоящем" вычитании. По этой причине для представления в памяти отрицательных чисел считается естественной их запись в виде "дополнения до двух".

Процедуру дополнения до двух производят с двоичными числами. Байт можно считать восьмизначным (восьмиразрядным) двоичным числом. В компьютерной терминологии каждый из таких разрядов принято называть битами и нумеровать их, как и разряды, справа налево, от 0 до 7. Так вот, чтобы получить дополнение числа до двух, нужно сначала обратить его биты, то есть заменить все нули единицами, а единицы нулями. Затем к полученному числу по правилам обычной арифметики (двоичной) прибавляется единица. Это все.

Если повторно применить операцию дополнения до двух к только что найденному результату, то получается исходное число. Можете считать это проявлением старого доброго правила "минус на минус дает плюс". И последнее: отрицательные целые числа отличаются от положительных тем, что у них старший разряд всегда равен 1. Его еще называют знаковым битом. Зная все это, мы сможем интерпретировать байты дампа как целые со знаком.

Первый байт равен CD=11001101, 8-й бит равен 1, то есть число отрицательное и нужно применять операцию дополнения до двух. Обращаем биты: 11001101 => 00110010; прибавляем единицу 00110010+1=00110011. Ответ: "-33" (шестнадцатеричное), или "-51" (десятичное). Второй байт дампа равен 20=00100000. Сразу понятно, что это положительное десятичное число 32. Кстати, хотя эти преобразования достаточно просты, чтобы делать их "в уме", для этого нужен определенный навык, так что поначалу проверяйте выкладки на бумаге.

Некоторые затруднения при интерпретации дампа памяти возникают, когда нас начинают интересовать числа с большим, чем два, количеством шестнадцатеричных разрядов. Во-первых, появляются новые термины. Так, два байта (четыре разряда) называют словом, четыре байта (восемь разрядов) - двойным словом, восемь байтов (16 разрядов) - четверным словом.

Во-вторых, возникает путаница из-за того, как мы записываем и читаем числа на бумаге. Мы не можем ни прочесть, ни записать число слева направо, пока не подсчитаем количество его разрядов, начиная с единиц, то есть справа налево. Иначе говоря, мы не можем читать число, как слово, не зная, какие символы встретим дальше. Так вот, байты одинарных, двойных и четверных слов располагаются в памяти так, что число читается слева направо: младшие байты (с младшими разрядами) расположены по младшему адресу, их можно читать, не зная, что будет дальше.

Уже рассмотренные правила представления и интерпретации чисел со знаком сохраняют свою силу. Так, слово памяти CD 20 нужно записать в виде числа, то есть как 20CD, что означает 8397 десятичное, как со знаком, так и без знака. Двойное слово CD 20 FF 9F следует переписать как 9FFF20CD. Это 2684297421 - десятичное число без знака, или 1610669875 - десятичное целое со знаком.

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

В двух словах: дробное десятичное число переводится в дробное двоичное число, которое нормализуется и записывается с показателем степени. Если в памяти для такого числа отведено четыре байта (32 бита), то мантисса располагается в битах с 0 по 22, порядок - с 23 по 30, а 31 бит - знаковый. Поскольку старший разряд нормализованной мантиссы всегда равен 1, то его опускают, увеличивая тем самым точность представления дробной части. Чтобы не выделять отдельного бита для хранения знака порядка, к нему всегда прибавляют 127, то есть показатели порядка, меньшие 127, считаются отрицательными.

Наконец, есть формат дробных чисел с фиксированной запятой. Он вовсе не поддается описанию. Во-первых, формат не поддерживается на уровне команд процессора. Для манипуляций такими числами используется целочисленная арифметика, а положение запятой держится "в уме". Во-вторых, формат поддерживается на уровне языков программирования, точнее, на уровне реализации компиляторов языков, предлагающих соответствующий тип чисел. Это объясняет, почему нет (насколько мне известно) стандартных соглашений о представлении чисел с фиксированной запятой.
Евгений Щербатюк


Компьютерная газета. Статья была опубликована в номере 41 за 1999 год в рубрике hard :: память

©1997-2021 Компьютерная газета