Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 980
Aisec Nory Avatar asked Jun 22 '21 20:06

Aisec Nory


Video Answer


2 Answers

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 232, 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.

like image 177
Eric Postpischil Avatar answered Sep 20 '22 15:09

Eric Postpischil


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)
like image 40
eerorika Avatar answered Sep 19 '22 15:09

eerorika