Поиск по сайту:

Учебное пособие по программированию на C для Linux, часть 15. Дополнение до 2 и отрицательные числа


На этой странице

  1. двухсекундное дополнение
  2. Отрицательные числа
  3. Заключение

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

Ну, это именно то, что будет обсуждаться в этом уроке. Итак, без лишних слов, давайте начнем с обсуждения.

2s дополнение

Прежде чем мы начнем с объяснения представления отрицательных чисел в памяти, важно, чтобы мы знали концепцию дополнения 1 и 2, обе из которых являются операциями двоичного уровня.

Возьмем очень простой пример. Предположим, у вас есть 4-байтовое целое число a с десятичным значением 15. Тогда вот как оно представлено в двоичной памяти:

00000000 00000000 00000000 00001111

Теперь, чтобы вычислить дополнение до единиц, просто инвертируйте все биты. Итак, ниже приведено представление 15 в дополнении к единице:

11111111 11111111 11111111 11110000

Теперь, если вы добавите 1 к приведенному выше двоичному представлению, вы получите дополнение 2s.

11111111 11111111 11111111 11110001

Таким образом, приведенное выше представление является дополнением до двух числа 15.

Отрицательные числа

Теперь некоторые из вас, должно быть, думают, почему мы обсуждали дополнение 1 и 2? Что ж, ответ заключается в том, что двоичное представление отрицательного числа вычисляется через дополнение до 2.

Сложно поверить? Вот доказательство:

Дополнение 2s, которое мы вычислили в предыдущем разделе, может быть представлено в шестнадцатеричной форме как 0xFFFFFFFF1. Теперь давайте посмотрим, что представляет собой это значение в десятичной форме с помощью программы C.

Вот код:

#include <stdio.h>

int main()
{
int a = 0xFFFFFFF1;
printf("a = %d", a);

return 0;
}

И следующий вывод:

a = -15

Веришь теперь? Мы начали с числа 15, вычислили его дополнение до 2, а когда мы снова преобразовали значение дополнения до двойки в десятичное число, мы нашли его -15.

Двигаясь дальше, теперь давайте немного подправим код, чтобы убедиться, что вызов printf считывает значение переменной a как целое число без знака.

#include <stdio.h>

int main()
{
int a = 0xFFFFFFF1;
printf("a = %u", a);

return 0;
}

Вот результат:

a = 4294967281

Упс, выход изменился, и теперь это огромное положительное значение. Но почему это произошло? Разве 0xFFFFFFF1 не является дополнением 15 до 2, как мы видели ранее?

Да, 0xFFFFFFFF1 – это двоичное дополнение к 15, но если вы не смотрите на это с этой точки зрения, это также нормальное значение (4294967281). Разница заключается в том, как это читается. Если оно читается как целое число со знаком (через %d в printf), вы увидите вывод как -15, но если оно читается как целое число без знака (через %u в printf), вы увидите вывод как 4294967281.

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

Наконец, обратите внимание, что вы также можете обратить представление дополнения до двух, чтобы получить его положительный аналог. В качестве примера снова возьмем значение 0xFFFFFFFF1, которое представляет собой шестнадцатеричное представление -15. Он представлен в двоичной форме как:

11111111 11111111 11111111 11110001

Теперь, чтобы получить положительный аналог, просто снова выполните 2-секундное дополнение. Это означает, что сначала выполните дополнение 1s:

00000000 00000000 00000000 00001110

А потом добавить 1

00000000 00000000 00000000 00001111

Теперь, если вы преобразуете это, вы получите значение 15 в десятичной форме.

Заключение

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