I am trying to understand the meaning of the statement:
(int)(unsigned)-1 == -1;
To my current understanding the following things happen:
-1is a signedintand is casted to unsignedint. The result of this is that due to wrap-around behavior we get the maximum value that can be represented by theunsignedtype.Next, this
unsignedtype maximum value that we got in step 1, is now casted tosigned int. But note that this maximum value is anunsigned type. So this is out of range of thesigned type. And since signed integer overflow is undefined behavior, the program will result in undefined behavior.
My questions are:
- Is my above explanation correct? If not, then what is actually happening.
- Is this undefined behavior as i suspected or implementation defined behavior.
PS: I know that if it is undefined behavior(as opposed to implementation defined) then we cannot rely on the output of the program. So we cannot say whether we will always get true or false.
CodePudding user response:
Cast to unsigned int wraps around, this part is legal.
Out-of-range cast to int is legal starting from C 20, and was implementation-defined before (but worked correctly in practice anyway). There's no UB here.
The two casts cancel each other out (again, guaranteed in C 20, implementation-defined before, but worked in practice anyway).
Signed overflow is normally UB, yes, but that only applies to overflow caused by a computation. Overflow caused by a conversion is different.
If the destination type is signed, the value does not change if the source integer can be represented in the destination type. Otherwise the result is:
(until C 20) implementation-defined
(since C 20) the unique value of the destination type equal to the source value modulo
2nwherenis the number of bits used to represent the destination type.(Note that this is different from signed integer arithmetic overflow, which is undefined).
More specifics on how the conversions work.
Lets's say int and unsigned int occupy N bits.
The values that are representable by both int and unsigned int are unchanged by the conversion. All other values are increased or decreased by 2N to fit into the range.
This conveniently doesn't change the binary representation of the values.
E.g. int -1 corresponds to unsigned int 2N-1 (largest unsigned int value), and both are represented as 11...11 in binary. Similarly, int -2N-1 (smallest int value) corresponds to unsigned int 2N-1 (largest int value 1).
int: [-2^(n-1)] ... [-1] [0] [1] ... [2^(n-1)-1]
| | | | |
| '---|---|-----------|-----------------------.
| | | | |
'---------------|---|-----------|----------. |
| | | | |
V V V V V
unsigned int: [0] [1] ... [2^(n-1)-1] [2^(n-1)] ... [2^n-1]
CodePudding user response:
Edit: Versions prior to C 20 do not require two's complement
It might help understanding the binary representation of -1 which is:
0b1111'1111'1111'1111'1111'1111'1111'1111. I should note, that this is a 4-byte representation of -1. Different machines might represent int differently.
You are casting to unsigned and then back to int which changes how you interpret the 1's and 0's. In decimal (or sometimes called denary), what you're doing is the equivalent of casting it to 4294967295 and then back to -1.
The type of the integer literal is the first type in which the value can fit, from the list of types which depends on which numeric base and which integer-suffix was used
Because -1 fits into int, that is how it's interpreted.
See: https://en.cppreference.com/w/cpp/language/integer_literal
this is out of range of the signed type. And since signed integer overflow
This is not integer overflow. This is binary representation. Integer overflow would be:
int int_max = 2147483647; // this is the maximum integer int on my machine
int one = 1;
int overflow = x s; // sum is equal to -2147483648
The above is an example integer overflow because the maximum representation
