When is integer signature really important?

Due to how transformations and operations are defined in C, it seems rarely matters whether you use a signed or unsigned variable:

uint8_t u; int8_t i; u = -3; i = -3; u *= 2; i *= 2; u += 15; i += 15; u >>= 2; i >>= 2; printf("%u",u); // -> 2 printf("%u",i); // -> 2 

So, is there a set of rules for determining under what conditions the significance of a variable really matters?

+4
source share
6 answers

In these contexts, it is important:

  • division and modulo: -2/2 = 1 , -2u/2 = UINT_MAX/2-1 , -3%4 = -3 , -3u%4 = 1
  • shifts. For negative signed values, the result >> and << is determined by the implementation or undefined, respectively. For unsigned values, they are always defined.
  • relational -2 < 0 , -2u > 0
  • overflowing. x+1 > x can always be considered as true by the compiler if if x has a signature type.
+8
source

Yes. Confirmation will affect the result of the Greater Than and Less Than statements in C. Consider the following code:

 unsigned int a = -5; unsigned int b = 7; if (a < b) printf("Less"); else printf("More"); 

In this example, More is not displayed correctly because -5 is converted by the compiler to a very high positive number.

It will also affect your arithmetic with variables of different sizes. Again, consider this example:

 unsigned char a = -5; signed short b = 12; printf("%d", a+b); 

The return result is 263, not expected 7. This is because -5 is actually treated by the compiler as 251. Overflow makes your operations correct for variables of the same size, but when expanded, the compiler does not expand the sign bit for unsigned variables, so it treats them as their original positive representation in a larger space. Examine how two compliments work and you will see where this result comes from.

+8
source

This affects the range of values ​​that you can store in the variable.

+6
source

This is mainly for comparison.

 printf("%d", (u-3) < 0); // -> 0 printf("%d", (i-3) < 0); // -> 1 
+3
source

An unsigned integer overflow simply wraps around. With signs with the default, this behavior is undefined, anything can happen.

+3
source

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 )))

+2
source

Source: https://habr.com/ru/post/1380030/


All Articles