Со школьной скамьи нас учили, что у чисел бывают отрицательные значения. Дело выглядело просто, записываешь число на бумаге, рисуешь черточку слева и все – положительное число превратилось в отрицательное. Для арифметической операции нужно от положительного числа отнять модуль отрицательного числа. К сожалению, такой подход совершенно не приемлем для машинных вычислений, потому что процессор не разделяет числа на положительные и отрицательные, а к числу нельзя просто пририсовать черточку. Да и хранить её негде Разве что «спрятать» в самом старшем бите.
В программировании на ассемблере числа без знака – любые числа, не имеющие знака минус в привычном представлении. То есть любые числа от нуля и выше. Для байта это означает любые числа от нуля до 255, для двух байт от 0 до 65535.
Хранение чисел со знаком урезает верхнюю границу хранимого в байте, двух байтах, четырех байтах и т.д. значения в два раза, потому что теперь старший бит занят знаком числа. Если этот бит равен нулю, то число положительное, а если бит равен единице – отрицательное.
Казалось бы, для хранения чисел со знаком нужно просто отвести старший бит под знак и этого достаточно (прямое кодирование числа со знаком). Но в истории развития ЭВМ для цели хранения числа со знаком помимо прямого кода использовали обратный и дополнительный код. Именно дополнительный код наиболее распространен в наше время, также он входит в архитектуру процессоров x86.
Дополнительный код как способ хранения чисел со знаком
Рассмотрим число 7. В двоичном виде оно представлено как 00000111.Чтобы превратить его в отрицательное значение, понятное процессору x86, необходимо конвертировать его в дополнительный код. Для этого все биты инвертируются (1 меняются на 0, а 0 на 1), а к младшему биту прибавляется единица:
Процесс перевода отрицательных чисел в положительные одинаков.
Синтаксис FASM
Обозначать привычные нам десятичные числа в коде программы можно с использованием знака минус «-», например:
1 | a db -7 |
Однако не только десятичные числа конвертируются в отрицательные с прибавлением «минуса», также такой приём проходит с числами других систем счисления и со строчными символами:
1 2 3 4 | y db -25h z db -77o k db -101b s db -'a' |
Как было написано выше, процессор не делит числа не положительные и отрицательное. Поэтому эта роль программиста определять, какими числами он оперирует в своей программе. Ведь от того интерпретируется байт как число без знака или как число со знаком, зависит его значение. Например, число со знаком -7 соответствует число без знака 249:
Диапазоны значений чисел со знаком и без
Числа без знака хранят в два раза большее положительные значения, чем это делают числа со знаком. Потому что числа со знаком также хранят отрицательные значения. Байт хранит 256 комбинаций из 8 бит, что соответствует 256 различным числам (включая 0). Граница верхнего хранимого числа в байте для числа без знака равна «255» (11111111b). Ну а для числа со знаком такой верхней границе соответствует число «127» (01111111b), а минимальному «-128» (10000000b). Схожим методом распределяется диапазон для переменных длинной в 2 байта и 4 байта.
В последующих уроках мы будем называть 16 битые переменные (2 байта) как слово, а 32 битые (4 байта) как двойное слово. Это общепринятые обозначения, которые относят нас к процессору Intel 8086 и его 16 битному такту, то есть к способности обработать 16 бит данных или одно слово (word) данных за такт. Следовательно, 32-битные переменные уже называются двойное слово (double word, dword). Ну а 32-битные процессоры просто унаследовали обозначения переменных от младшего брата. Отсюда же проистекают имена директив dw (Define Word) и dd (Define Dword). Ну а db – это Define Byte.
Для подкрепления урока приведу наглядную таблицу диапазонов чисел со знаком и без знака:
Размер переменной | Число без знака | Число со знаком | ||
---|---|---|---|---|
min | max | min | max | |
байт | 00000000 | 11111111 | 10000000 | 01111111 |
0 | 255 | -128 | 127 | |
слово | 00000000 00000000 | 11111111 11111111 | 10000000 00000000 | 01111111 11111111 |
0 | 65 535 | -32 768 | 32 767 | |
двойное слово | 0000…0000 | 1111…1111 | 1000…0000 | 0111…1111 |
0 | 4 294 967 295 | -2 147 483 648 | 2 147 483 647 | |
и т.д. | … | … | … | … |
Главный подводный камень любых операций с числами это выход за пределы хранимого в регистре или памяти диапазона. К примеру два больших положительных числа при сложении выйдут за свой диапазон и произведут отрицательное число! Из-за чего изменятся соответствующие флаги процессора, поведение и значение которых мы еще не раз рассмотрим в следующих уроках. Для избегания таких ситуаций программист должен оптимизировать операции с числами резервируя достаточное пространство под результат и т.д.
2 ответа к “Учебный курс. Урок 8. Числа со знаком и без”
Очень признателен за ваш труд, изучаю с первого урока, только первое знакомство с ассемблером еще 8 лет назад случилось. Но как бывает, без практики мозги особождают память) Так что теперь, можно сказать, заного изучаю ассемблер по вашим урокам. благо, материал подается легко. Еще когда учил раньше, не разобрался со знаковыми числами, или не старался особо, но когда нужно отладить программу там есть операторы перехода, учитывающие знак и не учитывающие, вот тогда требуется вникать в аргументы, есть там знак или его нету. Например, этот урок привел в порядок мозги, так что старые знания еще дают о себе знать)
Спасибо за такой лестный комментарий :)