найти на сайте
Главная2-й курс3-й курс4-й курс5-й курсСпецкурсыСсылкиКарта(версия для печати)

Машинное представление чисел

Краткие теоретические сведения

Во время изучения курса «Архитектура ЭВМ и язык ассемблера» практические примеры выполняются на базе архитектуры Intel x86, которая опирается на двоичную систему счисления. Базовые сведения по системам счисления приведены в материале

Для понимания способов представления чисел в ЭВМ можно ознакомиться со следующими материалами:

Также ниже дано краткое пояснение.

Конвертер

Введите число в форме записи:
в десятичной системе в двоичной системе в шестнадцатиричной системе

представление в прямом коде (с отдельным флагом знака числа)
представление в дополнительном коде (как в Intel x86)
Интерпретация значения
Байт (BYTE) без знака со знаком
 
 
 
 
Слово (WORD) без знака со знаком
 
 
 
 
Двойное слово (DWORD) без знака со знаком
 
 
 
 

Пояснение

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

10112... 121

Троичные системы счисления не получили распространения (существовали единичные образцы троичных ЭВМ) из-за сложности производства элементной базы, поэтому подавляющее число ЭВМ работают с числами в двоичной системе счисления.

01100... 001
b0 b1 b2 b3 b4 ... bk−2 bk−1 bk

В машинном представлении один двоичный разряд называется «бит» (от английского «BInary digiT» — bit, двоичная цифра). Один бит хранит мало информации, поэтому в оперативной памяти ЭВМ отдельные биты группируются в ячейки памяти, каждая из которых имеет свой адрес (или условный номер).

ячейка0
011... 1
b0 b1 b2 ... bk
  ячейка1
001... 1
b0 b1 b2 ... bk

. . .
ячейкаN
100...0
b0 b1 b2 ... bk
0 адреса ячеек → N

Таким образом, ячейка является минимально адресуемым объектом оперативной памяти ЭВМ, и чтобы прочитать или изменить отдельный бит в оперативной памяти, требуется прочитать или изменить соответствующую ячейку памяти целиком. Размер ячеек памяти (то есть количество бит в каждой ячейке) определяется архитектурой ЭВМ.

Группа из нескольких бит называется «байт» (от английского «BinarY digiT Eight» — byte). В большинстве случаев, байт состоит из 8 бит, но бывают и другие реализации, например: 6 или 7 бит. Поэтому когда надо отличить «правильный» 8-битовый байт от «неправильного», встречается термин «октет». В архитектуре Intel x86 оперативная память состоит из 8-битовых ячеек, поэтому для хранения одного 8-битового байта достаточно одной ячейки памяти.

Восемью двоичными разрядами можно описать 256 различных значений от 0 до 255. Для расширения диапазона обрабатываемых значений байты группируются в «слова» (два байта или 16 бит), «двойные слова» (четыре байта или 32 бита) и далее:

Понятно, что для обработки текстов в латинском и кириллическом алфавите достаточно выделять для хранения одной буквы по одному байту. Но для языков, набор знаков которых превышает 256, однобайтовой кодировки символов будет недостаточно.

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

На практике применяют второй вариант, который не только эффективно использует память, но и позволяет задействовать бит знака числа при работе с беззнаковыми неотрицательными числами. Если значение интерпретируется как число без знака, то бит знака числа трактуется как дополнительный разряд самого числа.

Таким образом получается, что при задействовании n битов памяти для обработки чисел их значения в диапазоне [0, 2n − 1 − 1] будут представлены в памяти одинаковым набором двоичных разрядов как для знаковых чисел, так и для беззнаковых, так как бит знака числа будет равен нулю. А беззнаковые значения в диапазоне [2n − 1, 2n − 1] будут неверно интерпретироваться в виде знаковых. И, наоборот, знаковые значения в диапазоне [−2n − 1, 2n − 1 − 1] будут неверно интерпретироваться в виде беззнаковых.

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

Тем не менее, есть способ, позволяющий для часто используемых операций сложения и вычитания обойтись единственным вариантом команд (остальные команды всё равно должны быть в двух комплектах). Для этого во многих архитектурах ЭВМ (и в архитектуре Intel x86, в частности) применяется двоичное представление в «дополнительном коде» для отрицательных значений:

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

Графически дополнительный код можно представить, как если бы отрезок [0, 2n] свернуть в кольцо и поверх него наложить отрезок [−2n − 1, 2n − 1 − 1]. Ниже приведён пример иллюстрации этого представления для 8-битового кода:

 -127

 129
-128

 128
127

127
126

126
 
-126 | 130  125 | 125
... | ... ... | ...
-3  | 253  3  |  3 
-2  | 254  2  |  2 
255

-1
0

0
1

1

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

Если выполянется условие x | < 2n − 1, y | < 2n − 1, тогда знаковые числа x и y можно представить в дополнительном коде в n-битной ячейке. В этом случае результат сложения чисел x и y, записанных в дополнительном коде, будет эквивалентен результату сложения этих чисел в беззнаковой интерпретации. При проверке надо помнить, что результат операций вычисляется по модулю 2n, потому что в n-битной ячейке можно хранить только n двоичных разрядов.

Почему числа «перевёрнутые»

Большинство ЭВМ (за некоторым исключением) размещают данные в памяти в порядке возрастания старшинства: младшие значения размещают в ячейках с меньшим адресом, а старшие значения — в ячейках с большим адресом. Такой подход упрощает обработку длинных данных, занимающих несколько ячеек памяти подряд.

X
X
011... 1
b0 b1 b2 ... bk
  X + 1
001... 1
b0 b1 b2 ... bk

. . .
X + M
100...0
b0 b1 b2 ... bk
0 адреса ячеек → N

Например, программа для сложения (или вычитания) двух длинных целых чисел Z = X + Y, занимающих 16 ячеек памяти, будет представлять собой цикл из 16 итераций, в каждой из которых будет производиться арифметическая операция с очередной парой ячеек памяти с учетом признака переполнения (OF).



X  → 


Y  → 


b0 b1 b2 ... bk
011... 1
+
001... 0
b0 b1 b2 ... bk
 
b0 b1 b2 ... bk
001... 1
+
110... 1
b0 b1 b2 ... bk
. . .
b0 b1 b2 ... bk
100...0
+
101...1
b0 b1 b2 ... bk

Z

(Z) = (X) + (Y)
010...0
b0 b1 b2 ... bk
  (Z + 1) = OF + (X+1) + (Y+1)
001...0
b0 b1 b2 ... bk

. . .
(Z + 15) = OF + (X+15) + (Y+15)
011...1
b0 b1 b2 ... bk
0 адреса ячеек → N

Максимальный эффект достигается тогда, когда требуется обрабатывать последовательность таких длинных чисел, которые не обязательно находятся в оперативной памяти ЭВМ или в устройстве с произвольным доступом. Действительно, если входные данные сплошным потоком поступают из устройства с последовательным доступом, например, из такого, как линия связи, магнитная лента или оптический диск, то, получив очередную ячейку данных, программа имеет возможность сразу её обработать, не дожидаясь прихода следующих данных.

А теперь внимание. Надеюсь, вы заметили в приведённых выше иллюстрациях, что адреса ячеек на иллюстрациях расположены в порядке возрастания слева-направо. Да, и биты тоже изображены в порядке возрастания старшинства разрядов слева-направо: слева — младший бит, а старший — справа. Но на письме мы записываем числа иначе. Сначала мы пишем старшие разряды, а потом младшие. Так вот, чтобы не было путаницы, и чтобы не тратить время на переворот записи числа в уме, для иллюстрации работы с числами ячейки памяти изображают в порядке возрастания адресов справа-налево. То есть справа изображают ячейки с младшими адресами, а слева — со старшими.

При таком подходе иллюстрация представления числа 46 901 (в двоичной записи — 1011 0111 0011 01012, в шестнадцатеричной записи — B7 3516) по адресу X в ячейках размером в байт будет выглядеть так:

B 7
1 0 1 1 0 1 1 1
b7 b6 b5 b4 b3 b2 b1 b0
X + 1
 
3 5
0 0 1 1 0 1 0 1
b7 b6 b5 b4 b3 b2 b1 b0
X
N ←  адреса ячеек 0

А представление числа 1 957 227 809 (111 0100 1010 1000 1110 1101 0010 00012, 74 A8 ED 2116) по адресу Y будет выглядеть так:

7 4
0 1 1 1 0 1 0 0
b7 b6 b5 b4 b3 b2 b1 b0
Y + 3
 
A 8
1 0 1 0 1 0 0 0
b7 b6 b5 b4 b3 b2 b1 b0
Y + 2
 
E D
1 1 1 0 1 1 0 1
b7 b6 b5 b4 b3 b2 b1 b0
Y + 1
 
2 1
0 0 1 0 0 0 0 1
b7 b6 b5 b4 b3 b2 b1 b0
Y
N ←  адреса ячеек 0