简体   繁体   中英

C++ unsigned and signed conversion

I've seen this kind of question asked before, but supplied answers don't clear everything for me. When this question is posted it is usually accompanied by next example:

#include <iostream>

int main()
{
    unsigned int u = 10;
             int i = -42;

    std::cout << i + i << std::endl;
    std::cout << i + u << std::endl;

    return 0;
}

Output:

-84
4294967264

All works as expected int is converted to unsigned. But if absolute value of i is less than u it seems like no such conversion is hapenning.

#include <iostream>

int main()
{
    unsigned int u = 10;
             int i = -3;

    std::cout << i + i << std::endl;
    std::cout << i + u << std::endl;

    return 0;
}

Output:

-6
7

I haven't seen any answer mentioning it and can't find any explanation. Even though it seems like a logical thing to happen I havent thing any explanation accounting for this.

After:

unsigned int u = 10;
         int i = -3;

the evaluation of i + u proceeds by first converting i to unsigned int . For a 32-bit unsigned int , this conversion wraps modulo 2 32 , which is 4,294,967,296. The result of this wrapping is −3 + 4,294,967,296 = 4,294,967,293.

After the conversion, we are adding 4,294,967,293 (converted i ) and 10 ( u ). This would be 4,294,967,303. Since this exceeds the range of a 32-bit unsigned int , it is wrapped modulo 4,294,967,296. The result of this is 4,294,967,303 − 4,294,967,296 = 7.

Thus “7” is printed.

But if absolute value of i is less than u it seems like no such conversion is hapenning.

Your assumption is wrong: such conversion is happening. And by "such conversion", I mean that when -3 was converted to the unsigned type, the result was 4'294'967'293.

I haven't seen any answer mentioning it and can't find any explanation.

Unsigned arithmetic is modular. This is simply how modular arithmetic works.

To understand modular arithmetic, think for example how a 12 hour clock works. It is also modular. You'll notice that the clock face doesn't have any negative numbers:

  • Time is 10 (am, today). 3 hours ago, what was the time? It was 7 (am, today).
  • Time is 10 (am, today). 42 hours ago, what was the time? It was 4 (pm, day before yesterday)

Unsigned arithmetic works exactly like that. Except, instead of 12 values, there are 4'294'967'296 values in case of 32 bit type.


In order to convert an unrepresentable value into the representable range, simply add or subtract the modulo (12 in case of clock, 4'294'967'296 in case of 32 bit unsigned integer) until the value is in the representable range.

Here is the math for the clock examples:

R ≡ 10 + (-3)          (mod 12)
// -3 = 9  + (12 * -1)
R ≡ 10 + 9             (mod 12) 
R ≡ 19                 (mod 12)
// 19 = 7  + (12 *  1)
R ≡ 7                  (mod 12)

R ≡ 10 + (-42)         (mod 12)
// -42 = 6 + (12 * -4)
R ≡ 10 + 6             (mod 12)
R ≡ 16                 (mod 12)
// 16 = 4 +  (12 *  1)
R ≡ 4                  (mod 12)

Here is the math for your examples:

R ≡ 10 + (-42)         (mod 4'294'967'296)
// -42           = 4'294'967'254 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'254 (mod 4'294'967'296)
R ≡ 4'294'967'264      (mod 4'294'967'296)

R ≡ 10 + (-3)          (mod 4'294'967'296)
// -3            = 4'294'967'293 + (4'294'967'296 * -1)
R ≡ 10 + 4'294'967'293 (mod 4'294'967'296)
R ≡ 4'294'967'303      (mod 4'294'967'296)
// 4'294'967'303 = 7             + (4'294'967'296 * -1)
R ≡ 7                  (mod 4'294'967'296)

There are two concepts very important to understanding how negative numbers and unsigned are related. The first is Ones compliment . This a very old standard and no longer used, but it simple to understand. Negative number are simply the not of each bit. -1 = ~1 = 1111...1110. The problem with ones complement is there are two zeros (+0/-0). This gets complicated very quickly. Consider

X = 2-2 Y = -2+2 X != Y

That's why modern computers and C/C++ adopted twos compliment . Twos compliment drops -0 (no such thing) and so

-1 = 1111...1111 -2 = 1111...1110 so -v = ~1+1

You see there is no "operation" to convert between signed and unsigned. In hardware each is just a vector of bits. Whether the value is signed or unsigned, is simply a question of how the bits are interpreted. To prove this you simply try

printf("%x", -1); // print a signed value as if it was unsigned

So to go back to your question, any small (+v) number is the same, whether it is signed or not. (signed) 1 is the same as unsigned (1). Only when the Most Significant Bit is one, does the interpretation change.

As an aside: in C/C++ "unsigned int" can be shortened to "unsigned" this is a remnant of the rule that any type that is not specified is assumed to be int.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM