Подписанность чисел дополнения 2 просто зависит от того, как вы интерпретируете число.Представьте себе 3-битные числа:
000
001
010
011
100
101
110
111
Если вы думаете, что 000
- ноль, а числа - как естественные для людей, вы бы интерпретировали их следующим образом:
000: 0
001: 1
010: 2
011: 3
100: 4
101: 5
110: 6
111: 7
Это называется "целое число без знака".Вы видите все как число, большее или равное нулю.
А что если вы хотите, чтобы некоторые числа были отрицательными?Ну, а 2-е дополнение приходит на помощь.Дополнение 2 известно большинству людей как просто формула, но на самом деле это всего лишь конгруэнтность по модулю 2 ^ n, где n - количество бит в вашем числе.
Позвольте мне привести несколько примеров соответствия:
2 = 5 = 8 = -1 = -4 module 3
-2 = 6 = 14 module 8
Теперь, просто для удобства, допустим, вы решили использовать в качестве знака самый левый бит числа.Итак, вы хотите иметь:
000: 0
001: positive
010: positive
011: positive
100: negative
101: negative
110: negative
111: negative
Просмотр ваших чисел конгруэнтно по модулю 2 ^ 3 (= 8), вы знаете, что:
4 = -4
5 = -3
6 = -2
7 = -1
Следовательно, вы просматриваете свои номера как:
000: 0
001: 1
010: 2
011: 3
100: -4
101: -3
110: -2
111: -1
Как видите, фактические биты для -3 и 5 (например) одинаковы (если число имеет 3 бита).Поэтому написание x = -3
или x = 5
дает тот же результат.
Интерпретация чисел с конгруэнтным модулем 2 ^ n имеет и другие преимущества.Если вы сложите 2 числа, одно отрицательное и одно положительное, на бумаге может случиться так, что у вас будет керри, который будет выброшен, но результат по-прежнему верен.Зачем?Этот перенос был 2 ^ n, который соответствует 0 по модулю 2 ^ n!Разве это не удобно?
Переполнение - это еще один случай конгруэнтности.В нашем примере, если вы сложите два беззнаковых числа 5 и 6, вы получите 3, что на самом деле равно 11.
Итак, почему вы используете подписанные и без знака?Для процессора на самом деле очень мало различий.Для you однако:
- Если число имеет n битов, беззнаковые представляют числа от 0 до 2 ^ n-1
- Если число имеет n битов, знак представляет числа от -2 ^ (n-1) до 2 ^ (n-1) -1
Так, например, если вы назначаете -1 для числа без знака, этото же самое, что присвоить ему 2 ^ n-1.
В соответствии с вашим примером это именно то, что вы делаете.вы назначаете -3 для uint8_t, что является недопустимым, но что касается процессора, вы назначаете ему 253.Тогда все остальные операции одинаковы для обоих типов, и в итоге вы получите один и тот же результат.
Однако есть момент, который ваш пример пропускает.оператор >>
на подписанном номере расширяет знак при переключении.Так как результат обеих ваших операций равен 9 до сдвига, вы этого не замечаете.Если бы у вас не было +15, вы бы получили -6 в i
и 250 в u
, что затем >> 2
приведет к -2
в i
(при печати с% u, 254) и62 в u
.(См. Комментарий Питера Кордеса ниже для некоторых технических деталей)
Чтобы лучше это понять, возьмите следующий пример:
(signed)101011 (-21) >> 3 ----> 111101 (-3)
(unsigned)101011 ( 43) >> 3 ----> 000101 ( 5)
Если вы заметили, слово (-21/8)на самом деле -3 и пол (43/8) равен 5. Однако -3 и 5 не равны (и не совпадают по модулю 64 (64, потому что есть 6 бит))