The subscription of two addition numbers is simply a matter of how you interpret the number. Imagine 3-bit numbers:
000 001 010 011 100 101 110 111
If you think of 000 as zero and numbers, since they are natural to humans, you should interpret them as follows:
000: 0 001: 1 010: 2 011: 3 100: 4 101: 5 110: 6 111: 7
This is called an "unsigned integer". You see everything as a number greater than or equal to zero.
Now, if you want some numbers to be negative? Well, 2 add-ons come to the rescue. 2 complement most people as a formula, but actually itβs just congruence modulo 2 ^ n, where n is the number of bits in your number.
Let me give you some examples of matching:
2 = 5 = 8 = -1 = -4 module 3 -2 = 6 = 14 module 8
Now, just for convenience, let's say you decide to have the leftmost bit of the number as your sign. So you want:
000: 0 001: positive 010: positive 011: positive 100: negative 101: negative 110: negative 111: negative
Viewing your numbers with a congruent module 2 ^ 3 (= 8), you know that:
4 = -4 5 = -3 6 = -2 7 = -1
Therefore, you view your numbers as:
000: 0 001: 1 010: 2 011: 3 100: -4 101: -3 110: -2 111: -1
As you can see, the actual bits for -3 and 5 (for example) are the same (if the number has 3 bits). Therefore, writing x = -3 or x = 5 gives the same result.
Interpretation of numbers comparable modulo 2 ^ n has other advantages. If you sum 2 numbers, one negative and one positive, it may happen on paper that you have a carry that will be thrown out, but the result is still correct. What for? This transfer was equal to 2 ^ n, which corresponds to 0 modulo 2 ^ n! Isn't that convenient?
Overflow is also another case of comparison. In our example, if you sum two unsigned numbers 5 and 6, you get 3, which is actually 11.
So why are you using signed and unsigned? There are actually very few differences for the CPU. For you :
- If a number has n bits, unsigned represents numbers from 0 to 2 ^ n-1
- If a number has n bits, then the sign represents numbers from -2 ^ (n-1) to 2 ^ (n-1) -1
So, for example, if you assign -1 to an unsigned value, this is the same as assigning it 2 ^ n-1.
According to your example, this is exactly what you are doing. you assign -3 to uint8_t, which is illegal, but as far as the processor is concerned, you assign 253 to it. Then all other operations are the same for both types, and you will get the same result.
There is, however, a point that your example misses. the >> operator on the signed number expands the sign when shifting. Since the result of both of your operations is 9, you will not notice this. If you did not have +15, you would have -6 in i and 250 in u , which then >> 2 would lead to -2 in i (if printed using% u, 254) and 62 in u . (See Peter Cordes Commentary below for a few technical questions)
To understand this better, take this example:
(signed)101011 (-21) >> 3 ----> 111101 (-3) (unsigned)101011 ( 43) >> 3 ----> 000101 ( 5)
If you notice that gender (-21/8) is actually -3, and gender (43/8) is 5. However, -3 and 5 are not equal (and do not correspond modulo 64 (64, because there are 6 bits )))