Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a negative integer summed with a greater unsigned integer promoted to unsigned int?

After getting advised to read "C++ Primer 5 ed by Stanley B. Lipman" I don't understand this:

Page 66. "Expressions Involving Unsigned Types"

unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264

He said:

In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.

But if I do something like this:

unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?

As you can see -10 is not converted to unsigned int. Does this mean a comparison occurs before promoting a signed integer to an unsigned integer?

like image 925
Alex24 Avatar asked Jan 14 '19 22:01

Alex24


People also ask

What happens if you put a negative number in an unsigned int?

You simply cannot assign a negative value to an object of an unsigned type. Any such value will be converted to the unsigned type before it's assigned, and the result will always be >= 0.

How do you represent negative numbers in unsigned?

Negative numbers however, are represented by taking the one's complement (inversion, negation) of the unsigned positive number. Since positive numbers always start with a “0”, the complement will always start with a “1” to indicate a negative number.

What happens when you add signed and unsigned int?

if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Does unsigned mean negative?

Unsigned means non-negative The term "unsigned" in computer programming indicates a variable that can hold only positive numbers. The term "signed" in computer code indicates that a variable can hold negative and positive values.


4 Answers

-10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.

like image 139
NathanOliver Avatar answered Oct 13 '22 03:10

NathanOliver


Well, I guess this is an exception to "two wrongs don't make a right" :)

What's happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result ends up being mathematically correct.

  • First, i is converted to unsigned and as per the wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.

  • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.


As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).


Important notice. According to C++ unsigned arithmetic does not overflow:

§6.9.1 Fundamental types [basic.fundamental]

  1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer 49

49) This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.

I will however leave "overflow" in my answer to express values that cannot be represented in regular arithmetic.

Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.

like image 32
bolov Avatar answered Oct 13 '22 02:10

bolov


i is in fact promoted to unsigned int.

Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get

[42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],

with [x] denoting the equivalence class of x in ℤ / 2nℤ.

Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate

[42] + [-10] ≡ [32]

would also be correct.

like image 6
Baum mit Augen Avatar answered Oct 13 '22 02:10

Baum mit Augen


"In the second expression, the int value -42 is converted to unsigned before the addition is done"

yes this is true

unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?

Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned

like image 3
bruno Avatar answered Oct 13 '22 01:10

bruno