3

I know that lower data types are cast into higher data types (e.g. int -> unsigned int -> float -> etc.) but I'm unsure of the following:

int var = 5u - 10; // var = -5
auto var = 5u - 10; // var = 4294967291

5u is unsigned but in the first case why -10 (signed integer) isn't converted to unsigned value while in the second case it does? In the first case the signed value isn't converted to an unsigned one and this is odd to me

Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123
Johnny Pauling
  • 12,701
  • 18
  • 65
  • 108

4 Answers4

4

There is no "signed integer literal": 5u - 10 is actually the subtraction of 10 from 5u.

The result (of the subtraction) is unsigned, and goes to overflow, giving as a result "5 numbers less than the overflown 0" (4294967291 = 232-5)

The first statement initialize an int, hence the unsigned compile time constant is reinterpreted as int. The result is correct (-5) because your hardware use 2s-complement arithmetic. (-5 and 4294967291 are the same 32 bit pattern)

The second statement initialize a variable whose type is inferred by the literal. And it is unsigned.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
3

Right-hand sides of both of your examples work entirely in the domain of unsigned type. I.e. both of your expressions 5u - 10 behave identically, which is not surprising, since they are the same. There's no conversion to int (as you seem to incorrectly assume) within the 5u - 10 expression in either case.

Expression 5u - 10 is always evaluated in the domain of unsigned type and produce unsigned result equal to UINT_MAX + 1 - 5. In the first initialization you attempt to force that value into a variable of type int, which results in overflow with implementation-defined behavior. In your case your implementation behaved so that var acquired value -5. In other words, the fact that you ended up with -5 in var has no definitive explanation in the domain of abstract C++ language. The result that you observe is just a quirk of your compiler. In some other compiler the first initialization might produce a different value in var.

In the second case the type of the expression (which is, again, unsigned) becomes the type of the variable, which is initialized with the unsigned value without any overflows.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

First of all, because you use auto, the compiler will pick unsigned in your second example.

Signed and unsigned numbers are stored the same way internally. It is just the way that the number is interpreted when it is printed that makes a difference [and in comparisons, as 'negative' signed numbers are lower than 0, where unsigned numbers can't be less than zero] - signed numbers are checked if they are 'negative', and printed as a minus sign and the negated original number. Unsigned numbers are just treated as whatever the internal representation becomes when printed.

So the values you see are just the two different representations of the same number - signed and unsigned respectively.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

In both C and C++, in the vast majority of cases, the type of an expression is determined from the expression itself, without regard to the context in which it appears.

int var = 5u - 10;

5u is of type unsigned int; 10 is of type int. The rules for the - operator cause the int argument to be converted to unsigned int, making the expression equivalent to 5u - 10u. The result is UINT_MAX + 1 - 10, a very large number. The initialization implicitly converts this from unsigned int to signed int. Since that value (almost certainly) isn't representable as an int, the result of the conversion is implementation-defined. In nearly all existing implementations, the conversion simply reinterprets the unsigned representation as if it were a signed value, resulting in -5. (This applies to systems that use a two's-complement representation for negative values; the resulting simplicity of signed/unsigned conversions is one of the reasons two's-complement is so widely used.)

Note that it would not be possible for var to have the value 4294967291; the largest value that an int can hold on your system is (probably) 2147483647.

auto var = 5u - 10;

5u - 10 is evaluated the same way as before, resulting in an unsigned int result of UINT_MAX + 1 - 5, or 4294967291 on your system. The auto means that var takes on the type of the expression, unsigned int, so no conversion is performed. (On a system with 16-bit int, the result would be 65531.)

In answer to your question, the constant 10 is converted from int to unsigned int in both cases.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631