Почему предпочтение дополнения двух над знаком и величиной для подписанных чисел?

Мне просто интересно, есть ли причина, по которой для представления -1 в двоичном коде используется дополнение two: переворачивание битов и добавление 1?

-1 представлен 11111111 (дополнение двух), а не (для меня более интуитивным) 10000001, который является двоичным 1 с первым битом в качестве отрицательного флага.

отказ от ответственности: я не полагаюсь на двоичную арифметику для моей работы!

18 ответов


это сделано так, что сложение не должно иметь никакой специальной логики для работы с отрицательными числами. Проверьте статья в Википедии.

скажем, у вас есть два числа, 2 и -1. В вашем "интуитивном" способе представления чисел они будут 0010 и 1001, соответственно (я придерживаюсь 4 бит для размера). В дополнение к двум путям, они 0010 и 1111. Теперь, скажем, я хочу добавить их.

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

  0010
+ 1111
=10001
= 0001 (discard the carry)

0001 1, что является ожидаемым результатом "2+(-1)" быть.

но в вашем "интуитивном" методе добавление сложнее:

  0010
+ 1001
= 1011

что такое -3, верно? Простое сложение в данном случае не работает. Вы должны отметить, что одно из чисел отрицательно и использовать другой алгоритм, если это так.

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

кроме того, в "интуитивном" методе хранения есть два нуля:

0000  "zero"
1000  "negative zero"

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

есть еще один бонус с хранением ints таким образом, и именно тогда вам нужно расширить ширину регистра, в котором хранится значение. С дополнением two хранение 4-битного числа в 8-битном регистре-это вопрос повторения его самого значительного бита:

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1110 (negative two, in four bits)
11111110 (negative two, in eight bits)

это просто вопрос, глядя на знак бит меньшего слова и повторяя его, пока он не заполнит ширину большего слова.

С помощью вашего метода вам нужно будет очистить существующий бит, что является дополнительной операцией в дополнение к заполнению:

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1010 (negative two, in four bits)
10000010 (negative two, in eight bits)

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


Википедия говорит:

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

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


хотя этот вопрос старый, позвольте мне положить мои 2 цента.

прежде чем я объясню это ,давайте вернемся к истокам. 2 ' дополнение-это дополнение 1 + 1 . Теперь, что такое дополнение 1 и каково его значение в дополнение.

сумма любого n-битного числа и его дополнения 1 дает вам максимально возможное число, которое может быть представлено этими n-битами. Пример:

 0010 (2 in 4 bit system)
+1101 (1's complement of 2)
___________________________
 1111  (the highest number that we can represent by 4 bits)

теперь, что произойдет, если мы попытаемся добавить еще 1 к результату. Его результаты в переполнении.

результат будет 1 0000 который равен 0 (поскольку мы работаем с 4-битными числами, (1 слева-переполнение )

и

Any n-bit number + its 1's complement = max n-bit number
Any n-bit number + its 1'complement + 1 = 0 ( as explained above, overflow will occur as we are adding 1 to max n-bit number)

кто-то решил назвать дополнение до 1 + 1 как 2'complement. Поэтому вышеуказанное заявление будет: Любое число n'bit + его дополнение 2 = 0 что означает дополнение 2 числа = - (этого числа)

все это дает еще один вопрос , почему мы можем использовать только в (N-1) из n бит представляют положительное число и почему самый левый N-й бит представляет знак (0 на самом левом биту означает +ve число, а 1 означает-ve число). например, почему мы используем только первые 31 бит int в java для представления положительного числа, Если 32-й бит равен 1, его число a-ve.

 1100 (lets assume 12 in 4 bit system)
+0100(2's complement of 12)
___________________________

1 0000 (результат равен нулю, при переносе 1 переполнение)

таким образом , система (n + 2'complement n) = 0, все еще работает. Только здесь неоднозначность 2 дополнения до 12 0100 который неоднозначно также представляет +8, кроме представления -12 в системе дополнения 2s.

эта проблема будет решена, если положительные числа всегда 0 в их осталось всего немного. В этом случае их дополнение 2 всегда будет иметь 1 в их левом большинстве бит , и у нас не будет двусмысленности того же набора битов, представляющих число дополнения 2, а также число +ve.


дополнение позволяет сложение и вычитание, чтобы быть сделано обычным способом (как вы ранить для беззнаковых чисел). Он также предотвращает -0 (отдельный способ представления 0, который не был бы равен 0 при обычном методе сравнения чисел бит за битом).


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


обычная реализация операции - "перевернуть биты и добавить 1", но есть другой способ ее определения, который, вероятно, делает обоснование более ясным. Дополнение 2-это форма, которую вы получаете, Если вы берете обычное беззнаковое представление, где каждый бит контролирует следующую степень 2, и просто делаете наиболее значимый термин отрицательным.

принимая 8-битное значение a7 a6 a5 a4 a3 a2 а1 a0

обычная двоичная интерпретация без знака:
27 * a7 + 26 * a6 + 25 * a5 + 24 * a4 + 23 * a3 + 22 * a2 + 21 * a1 + 20 * a0
11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

интерпретация дополнения двух:
-27 * a7 + 26 * a6 + 25 * a5 + 24 * a4 + 23 * a3 + 22 * a2 + 21 * a1 + 20 * a0
11111111 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1

ни один из другие биты меняют значение вообще и переносятся в7 является "переполнением" и не ожидается, что он будет работать, поэтому почти все арифметические операции работают без изменений (как отмечали другие). Знак-величина вообще проверяет бит знака и использует различную логику.


чтобы расширить на других ответы:

В дополнение

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

разделение требует другого механизма.

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


дополнение 2 позволяет отрицательным и положительным числам быть добавленным совместно без любой специальной логики.

Если вы пытались добавить 1 и -1, используя свой метод
10000001 (-1)
+00000001 (1)
вы получите
10000010 (-2)

вместо этого, используя дополнение two, мы можем добавить

11111111 (-1)
+00000001 (1) вы получите
00000000 (0)

то же самое верно для вычитания.

кроме того, если вы пытаетесь вычесть 4 из 6 (два положительных числа), вы можете дополнить 4 2 и добавить два вместе 6 + (-4) = 6 - 4 = 2

Это означает, что вычитание и сложение как положительных, так и отрицательных чисел может выполняться одной и той же схемой в ЦП.


читать ответы на этот вопрос, я наткнулся на этот комментарий [отредактировано].

дополнение 2 0100(4) будет 1100. Теперь 1100 - это 12, Если я говорю нормально. Так, когда я говорю normal 1100, то это 12, но когда я говорю 2's complement 1100, то это -4? Кроме того, в Java, когда 1100 (предположим, 4 бита на данный момент) хранится тогда как это определяется, если это +12 или -4 ?? – hagrawal 2 июля в 16:53

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

вопрос-как система может установить, как один или несколько соседних байтов должны быть интерпретированы? В частности, как система может установить, является ли данная последовательность байтов простым двоичным числом или дополнительным числом 2?

ANSWER-система устанавливает, как интерпретировать последовательность байтов через типы. Типы определить

  • сколько байтов нужно учитывать
  • как эти байты должны быть истолкованы

пример-ниже мы предполагаем, что

  • charС 1 байт
  • shortС 2 байт
  • intи floatС 4 байт

обратите внимание, что эти размеры относятся к моей системе. Хотя они довольно распространены, они могут отличаться от system системы. Если вам интересно, что они находятся в вашей системе, используйте оператор sizeof.

прежде всего мы определяем массив, содержащий 4 байта, и инициализируем их все двоичным числом 10111101, соответствующее шестнадцатеричному числу BD.

// BD(hexadecimal) = 10111101 (binary)
unsigned char   l_Just4Bytes[ 4 ]   =   { 0xBD, 0xBD, 0xBD, 0xBD };

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

unsigned char и signed char

// 10111101 as a PLAIN BINARY number equals 189
printf( "l_Just4Bytes as unsigned char  -> %hi\n", *( ( unsigned char* )l_Just4Bytes ) );

// 10111101 as a 2'S COMPLEMENT number equals -67
printf( "l_Just4Bytes as signed char    -> %i\n", *( ( signed char* )l_Just4Bytes ) );

unsigned short и short

// 1011110110111101 as a PLAIN BINARY number equals 48573
printf( "l_Just4Bytes as unsigned short -> %hu\n", *( ( unsigned short* )l_Just4Bytes ) );

// 1011110110111101 as a 2'S COMPLEMENT number equals -16963
printf( "l_Just4Bytes as short          -> %hi\n", *( ( short* )l_Just4Bytes ) );

unsigned int, int и float

// 10111101101111011011110110111101 as a PLAIN BINARY number equals 3183328701
printf( "l_Just4Bytes as unsigned int   -> %u\n", *( ( unsigned int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a 2'S COMPLEMENT number equals -1111638595
printf( "l_Just4Bytes as int            -> %i\n", *( ( int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a IEEE 754 SINGLE-PRECISION number equals -0.092647
printf( "l_Just4Bytes as float          -> %f\n", *( ( float* )l_Just4Bytes ) );

4 байта в ОЗУ (l_Just4Bytes[ 0..3 ]) всегда остаются точно такими же. Единственное, что меняется, это то, как мы интерпретируем их.

опять мы система как интерпретировать их через типы.

например, выше мы использовали следующие типы интерпретировать содержание l_Just4Bytes массив

  • unsigned char: 1 байт в обычном двоичном коде
  • signed char: 1 байт в дополнении 2
  • unsigned short: 2 байта в простой двоичной нотации
  • short: 2 байта в дополнении 2
  • unsigned int: 4 байта в простой двоичной нотации
  • int: 4 байта в дополнении 2
  • float: 4 байта в IEEE 754 с одной точностью

[EDIT] этот пост имеет отредактировано после комментария user4581301. Спасибо, что нашли время, чтобы бросить эти несколько полезных строк!


вы можете посмотреть, как профессор Джерри Кейн из Стэнфорда объясняет дополнение двух, во второй лекции (объяснение относительно дополнения 2 начинается около 13:00) в серии лекций под названием парадигмы программирования, доступные для просмотра с канала YouTube Стэндфорда. Вот ссылка на серию лекций:http://www.youtube.com/view_play_list?p=9D558D49CA734A02.


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

Если есть X бит, дополнение two будет варьироваться от +(2^x/2+1) до- (2^x/2). Его дополнение будет работать от +(2^x/2) до- (2^x/2), но позволит отрицательный ноль (0000 равен 1000 в системе дополнения 4 бит 1).


Ну, ваше намерение на самом деле не в том, чтобы отменить все биты вашего двоичного числа. На самом деле это вычесть каждую свою цифру из 1. Это просто счастливое совпадение, что вычитание 1 из 1 приводит к 0 и вычитание 0 из 1 приводит к 1. Таким образом, переворачивание бит эффективно выполняет это вычитание.

но почему вы находите разницу каждой цифры от 1? А ты нет. Ваше фактическое намерение-вычислить разницу данного двоичного числа от другого двоичного числа который имеет такое же количество цифр, но содержит только 1. Например, если ваш номер 10110001, когда вы переворачиваете все эти биты, вы эффективно вычисляете (11111111 - 10110001).

Это объясняет первый шаг в вычислении дополнения Two. Теперь давайте включим второй шаг-добавление 1-также в картину.

добавить 1 к приведенному выше двоичному уравнению:

11111111 - 10110001 + 1

Что вы получите? Это:

100000000 - 10110001

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

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


мы выполняем только операции сложения для сложения и вычитания. Мы добавляем второй операнд к первому операнду для добавления. Для вычитания мы добавляем дополнение 2 второго операнда к первому операнду.

с представлением дополнения 2 нам не нужны отдельные цифровые компоненты для вычитания-используются только сумматоры и комплементеры.


стоит отметить, что на некоторых ранних суммирующих машинах, до дней цифровых компьютеров, вычитание будет выполняться путем ввода оператором значений с использованием другого цветного набора легенд на каждой клавише (так что каждая клавиша будет вводить девять минус число, которое будет вычитаться), и нажмите специальную кнопку, будет предполагать перенос в расчет. Таким образом, на шестизначной машине, чтобы вычесть 1234 из значения, оператор нажимал клавиши, которые обычно указывали бы " 998,765" и нажмите кнопку, чтобы добавить это значение плюс один к текущему вычислению. Суммы. это просто двоичный эквивалент, что ранее "десять дополнения" арифметика.


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


основным преимуществом представления дополнения двух, которое еще не упоминалось здесь, является то, что нижние биты суммы дополнения двух, разницы или продукта зависят только на соответствующие биты операндов. Причина, по которой 8-битное знаковое значение для -1 -11111111 заключается в том, что вычитая любой целое число, наименьшие 8 бит которого 00000001 из любого другого целого числа, чьи низкие 8 битов 0000000 даст целое число, наименьшие 8 бит которого 11111111. Математически значение -1 будет бесконечной строкой из 1, но все значения в диапазоне определенного целочисленного типа будут либо все 1, либо все 0, прошедшие определенную точку, поэтому компьютерам удобно "подписывать" самый значительный бит числа, как если бы он представлял бесконечное число 1 или 0.

Two-complement - это почти единственное представление со знаком, которое хорошо работает при работе с типами, большими, чем двоичная машина естественный размер слова, так как при выполнении сложения или вычитания код может получить самый низкий кусок каждого операнда, вычислить самый низкий кусок результата и сохранить его, затем загрузить следующий кусок каждого операнда, вычислить следующий кусок результата и сохранить его и т. д. Таким образом, даже процессор, который требует, чтобы все дополнения и вычитания проходили через один 8-битный регистр, может обрабатывать 32-битные подписанные числа достаточно эффективно (медленнее, чем с 32-битным регистром, конечно, но все же осуществимый.)

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


потому что производители процессоров ленивые!


один удовлетворительный ответ о том, почему дополнение Two2 используется для представления отрицательных чисел, а не Системы дополнения, заключается в том, что Система дополнения Two решает проблему несколько представлений 0 и необходимость end-around-carry которые существуют в одной системе дополнения представления отрицательных чисел.

для получения дополнительной информации посетите https://en.wikipedia.org/wiki/Signed_number_representations

для конца-вокруг-носит посещение https://en.wikipedia.org/wiki/End-around_carry