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

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

В языке C их объявления выглядят следующим образом:

/* Signed Integer */
int n = -2;
/* Unsigned Integer */
unsigned int m = 2;

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

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

Числовые значения, хранящиеся в памяти, представлены в виде битов (0 или 1) в группах байтов (8 бит).

Число, такое как 2, сохраняется в памяти как

00000000 00000000 00000000 00000010

Что означает 4 байта памяти для целого числа со знаком.

Но как насчет отрицательных чисел? Как язык C понимает, что число имеет отрицательное значение или нет?

Здесь используется двойное дополнение двоичного файла. Дополнение — это преобразование двоичного представления числа, которое позволяет отображать отрицательные значения, для понимания дополнения до двух мы должны сначала увидеть дополнение до единицы.

Дополнение 1 или Дополнение 1

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

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

Пример:

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

На языке C, используя printf, мы получили бы:

#include <stdio.h>
int main()
{
/* Binary of 0: 00000000 */
    printf("Value 0: %d\n", 0); 
/* Binary of ~0: 11111111 */
    printf("Value ~0: %d\n", ~0);
/* Binary of 1: 00000001 */
    printf("Value 1: %d\n", 1);
/* Binary of ~1: 11111110 */
    printf("Value ~1%d\n", ~1);
/* Binary of 2: 00000010*/
    printf("Value 2: %d\n", 2);
/* Binary of ~2: 11111101*/
    printf("Value ~2: %d\n", ~2);
    return 0;
}
Value 0: 0
Value ~0: -1
Value 1: 1
Value ~1: -2
Value 2: 2
Value ~2: -3

Проблема с этим представлением заключается в том, что 0 имеет как положительное, так и отрицательное значение -0 и +0.

Дополнение до двух или Дополнение до двух

Это еще один метод, подобный дополнению до единицы, для представления отрицательных двоичных чисел при наличии переменной типа со знаком. В дополнении до двух положительные числа остаются неизменными по сравнению с их базовым двоичным представлением. Однако отрицательное число представлено двоичным числом, которое при добавлении к соответствующему положительному эквиваленту дает ноль. Проще говоря, дополнение числа до двух равно его дополнению до единицы + 1.

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

Если, например, у нас есть число 5, то его двоичное представление будет таким:

00101
Obtaining the One´s complement would result in
01010
And adding 1 (00001) would result in 
01011
Positive 5 Would have a first bit in 0 and Negative 5 would have this bit in 1 
01011 (+5)
11011 (-5)

Это будет визуализировано следующим образом:

MSB всегда равен 1 в случае отрицательных чисел и 0 в случае положительных чисел.

Диапазон чисел:

Значения, представленные таким образом в nразрядном регистре памяти, могут иметь положительное число до

{[2 ^ (n-1)] - 1}

, и отрицательное число от

-[2^(n-1)].

Арифметика с дополнением до 2:

Одним из преимуществ представления значений со знаком с дополнением до 2 является возможность легкого выполнения двоичной арифметики над числами со знаком или без знака. В результате получается правильное дополнение до двойки.

Дополнение:

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

          0000 1000
        + 1111 1101
         ----------
Carry 1 <-0000 0101 = (+5)

Поскольку перенос равен 1, то число положительное (бит со знаком).

Здесь перенос равен 1, но он выходит за пределы байта, поэтому он не учитывается в ответе, и результатом является положительное число длиной в один байт.

Вычитание:

Подобно сложению, но здесь вычитаемое сначала преобразуется в отрицательную форму, а затем добавляется с уменьшаемым.

Пример:

          0000 1000
        - 1111 0111
         ----------
Carry 0<-1111 1111 = (-1)

Поскольку перенос равен 0, то число отрицательное (бит со знаком).

Умножение:

Это следует тем же правилам, что и базовое двоичное умножение.

Пример:

          1111 1100 (+4)
        * 0000 0100 (-4)
         ----------
Carry 0<-1111 0000=(-16)

Поскольку перенос равен 0, то число отрицательное (бит со знаком).

Отдел:

Для деления это состоит из многократного повторения вычитания дополнения до двух.

  • Сначала вычислите дополнение до 2 делителя, а затем этот преобразованный делитель должен быть добавлен к делимому.
  • Теперь идет следующий цикл вычитания. Здесь частное заменяет делимое.
  • Это повторяется снова и снова, пока частное не станет слишком маленьким или равным нулю. Если он не равен нулю, то он считается остатком.

Пример:

          0000 0111 (+7)
        / 1111 1101 (-3)
          ----------
          0000 0100 = (+4)
          0000 0100 (+4)
        / 1111 1101 (-3)
          ----------
          0000 0100 = (+1) Remainder

Знаковый бит равен 0, поэтому остаток положительный.

Переполнение:

Значения со знаком в языках программирования имеют проблему с размером

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

Если, например, у нас есть пределы -10 и 9 в памяти

  • Значение -11 покажет 9
  • Значение -12 покажет 8

И так далее…

  • Значение 10 покажет -10
  • Значение 11 покажет -9