Integer promotion example in C [duplicate] - c

This question already has answers here:
Implicit type promotion rules
(4 answers)
Type conversion - unsigned to signed int/char
(5 answers)
Closed 4 years ago.
void foo(void) {
unsigned int a = 6;
int b = -20;
if (a+b > a) {
printf("> a");
} else {
printf("< a");
}
}
I am trying to understand what is going on with the integer promotion example above. I know that for a = 6 and b = -20 the output should be > a due to b being promoted to unsigned int. However, the output goes to < a if I assign b = -5. Shouldn't the output be the same in this case as well since the value b = -5 is also promoted to an unsigned int?

The reason for this has to do with the method in which the signed value is converted to unsigned.
Section 6.3.1.3 of the C standard regarding conversions of signed and unsigned integers dictates how this occurs:
2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.60)
...
60) The rules describe arithmetic on the mathematical value, not the value of a given type of expression.
In your example with b equal to -20, when it is converted to unsigned UINT_MAX + 1 is added to the value, so the converted value is UINT_MAX - 19. When you then add the value of a (6), you get UINT_MAX - 13. This value is larger than a, so "> a" is printed.
If you set b equal to -5, the converted value is UINT_MAX - 4. Adding 6 to this gives you UINT_MAX + 2. Since math on unsigned int values occurs modulo UINT_MAX + 1, the actual result is 1. This is less than 6, so "< a" is printed.
Also, what is happening here is not integer promotion but integer conversion. Promotion happens first if any integer type in an expression has a rank less than int. That is not the case here.

Related

Why I can assign a negative value to an unsigned int data type?

I was doing some experiments in a code in order to prove the theory. This is my code:
#include <stdio.h>
int main(){
unsigned int x,y,z;
if(1){
x=-5;
y=5;
z=x+y;
printf("%i",z);
}
return 0;
}
But for what I know the output should have been 10, but instead it prints 0, why this is happening? why I can assign a negative value to an unsigned int data type
From section 6.5.16.1 of the C standard:
In simple assignment (=), the value of the right operand is converted
to the type of the assignment expression and replaces the value stored
in the object designated by the left operand.
From section 6.3.1.3 Signed and unsigned integers:
... Otherwise, if the new type is unsigned, the value is converted
by repeatedly adding or subtracting one more than the maximum value
that can be represented in the new type until the value is in the
range of the new type.
So x=-5 assigns UINT_MAX + 1 - 5 to x.
Signed to unsigned conversion happens as per "mathematic modulus by UINT_MAX+1", from C17 6.3.1.3:
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type
Now as it happens, on 2's complement computers this is the same as taking the binary representation of the signed number and treat it like an unsigned number. In your case -5 is represented as binary 0xFFFFFFFB, so the unsigned number ends up as 4294967291. And 4294967291 + 5 creates an unsigned wrap-around from UINT_MAX = 4294967295 to 0 (which is well-defined, unlike signed overflow).
So -5 does not just discard the sign when converted to unsigned. If that's what you want to happen, use the abs() function from stdlib.h.
It's a basic characteristic of pretty much all integral types that they have a defined range of values they can hold. But then the question is, what happens if you try to set a value outside that range? For C's unsigned types, the answer is that they operate via modular arithmetic.
On a modern machine, type unsigned int probably has a range of 0 to 4294967295. Obviously -5 does not fit into that range. So modular arithmetic says that we add or subtract some multiple of 4294967296 until we get a value that is in the range. Now, -5 + 4294967296 is 4294967291, and that is in range, so that's the value which gets stored in your variable x.
So then x + y will be 4294967296, but that's not in the range of 0 to 4294967295, either. But if we subtract 4294967296, we get 0, and that's in range, so that's our answer.
And along the way we've discovered how two's complement arithmetic works. It turns out that, if we had declared x as a signed int and set it to -5, it would have ended up containing the same bit pattern as 4294967291. And as we've seen, 4294967291 is precisely the bit pattern we want to be able to add to 5 in order to get 0 (after wrapping around, that is). So 4294967291 is a great internal value to use for -5, since you obviously want -5 + 5 to be 0.
Why i can assign a negative value to an unsigned int data type?
Assigning an out-of-range value to an unsigned type is well defined.
But first, in trying to report the value, code invokes undefined behavior (UB) using a mismatched printf() specifier with unsigned:
// printf("%i",z); // Bad
printf("%u\n",z); // Good
Try printf("%u %u %u %u\n", x, y, z, UINT_MAX); to properly see all 4 values.
x=-5; assigns an out-of-range value to an unsigned. With unsigned types, the value is converted to an in-range value by adding/subtracting the max value of the type + 1 until in range. In this case x will have the value of UINT_MAX + 1 - 5.
y=5; is OK.
x+y will then incur unsigned math overflow. The sum is also converted to an in-range value in a like-wise manner.
x+y will have the value of (UINT_MAX + 1 - 5) + 5 --> (UINT_MAX + 1 - 5) + 5 - (UINT_MAX + 1) --> 0.

Why the value of signed char is greater than the unsigned int [duplicate]

This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 1 year ago.
#include <stdio.h>
int main() {
unsigned int i = 23;
signed char c = -23;
if (i<c)
puts("TRUE");
return 0;
}
Why the output of the following program is TRUE, even though I have used signed char which can store from -128 to 127.
Your i < c comparison has operands of two different types, so the operand with the smaller type (lower rank) is converted to the type of the other. In this case, as -23 cannot be represented as an unsigned int, and following the "usual arithmetic conversions," it will end up with a value considerably greater than +23 (actually, UINT_MAX + 1 - 23), according to Paragraph #2 in the following excerpt from this (Draft) C11 Standard:
6.3.1.3 Signed and unsigned integers
1    When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
2    Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.
3    Otherwise, the new type is signed and the
value cannot be represented in it; either the result is
implementation-defined or an implementation-defined signal is
raised.
You can see this conversion in action by assigning c to a separate unsigned int and printing its value, as in the following code. Assuming a 32-bit unsigned int, you will likely see a value of 4294967273 (which is, indeed, greater than 23).
#include <stdio.h>
int main()
{
unsigned int i = 23;
signed char c = -23;
unsigned int uc = c; // Add a diagnostic line ...
printf("%u\n", uc); // ... and show the value being used in the comparison
if (i < c) {
printf("TRUE");
}
return 0;
}
Note: The relative size of the two variables (i and c) here is something of a red herring; even if you declare c as signed int c = -23;, you will still get the same result, due to the Usual Arithmetic Conversions (link courtesy of Jonathan Leffler's comment) – note that a signed int cannot represent all possible values of an unsigned int, so the very last of the 'hollow' bullets will come into play.
In order to make the comparison, the smallest variable (char, 8 bit) must be promoted to the size and signedness of the larger one (integer, 32 bit). But in so doing, -23 becomes 4294967273. Which is much more than 23.
If you compare c with a signed integer, the sign promotion causes no change and the test behaves as expected.

What happens when evaluating `int x = -2147483648`?

What happens when evaluating
int x = -2147483648
?
When evaluating -2147483648, 2147483648 is a integer constant which has long type not int, so the result -2147483648 of evaluating -2147483648 is of type long, not int.
When evaluating the assignment "int x = ...", the RHS is value -2147483648 of long type, which is in the range of x's type int. Will value -2147483648 be implicitly converted from long to int, and the conversion keep the value -2147483648 unchanged?
Thanks.
Your analysis in this question is right. Because the value is in the range of int and is converted to int as part of the initialization (note: the same would happen for assignment), everything works as intended.
As for the hidden part of your question you didn't ask, since it keeps getting closed as a duplicate, this does not mean you can define INT_MIN as -2147483648. The magic is in the = (as assignment operator or a token in the initialization construct). In contexts where it's not being used, there are all sorts of ways that -2147483648 having type long or long long rather than int breaks semantic requirements on INT_MIN. For example:
(INT_MIN < 0U) == 0 (because both operands are promoted to unsigned), but
(-2147483648 < 0U) == 1 (because both operands are promoted to long or long long).
This answer presumes the C implementation uses a 32-bit int and a 64-bit long.
C 2018 6.4.4.1 says “The type of an integer constant is the first of the corresponding list in which its value can be represented.” In the table that follows, the entry for decimal constants with no suffix contains the list int, long int, long long int. Since a long int is the first of those that can represent 2,147,483,648, 2147483648 has the type long int.
Per 6.5.3.3 3, the result of - is the promoted type. The integer promotions (6.3.1.1 2) have no effect on long int. So the type of -2147483648 is long int.
Per 6.7.9 11, the value of the initializer is converted as in simple assignment. Per 6.5.16.1 2 and 6.5.16 3, the value is converted to the type of the object being assigned would have after lvalue conversion. That is, for an assignment to an int object, the type is an int value.
Per 6.3.1.3 1, when converting a value of integer type to another integer type, if the new type can represent the value, it is unchanged. Since int can represent −2,147,483,648, it is unchanged.
Therefore, the result of int x = -2147483648; is that x is initialized with the value −2,147,483,648.

Why this program is output Yes, while it should either have thrown error or NO [duplicate]

This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 5 years ago.
Here a + b is -14 which should have been lesser than 'a' and hence should have printed NO but it printed yes.
unsigned int a = 6;
int b = -20;
if((a+b) > a){
printf("Yes");
} else {
printf("NO");
}
return 1;
According to the C Standard (6.3.1.8 Usual arithmetic conversions)
1 Many operators that expect operands of arithmetic type cause
conversions and yield result types in a similar way. The purpose is to
determine a common real type for the operands and result. For the
specified operands, each operand is converted, without change of type
domain, to a type whose corresponding real type is the common real
type. Unless explicitly stated otherwise, the common real type is also
the corresponding real type of the result, whose type domain is the
type domain of the operands if they are the same, and complex
otherwise. This pattern is called the usual arithmetic conversions:
...
Otherwise, 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.
Types unsigned int and int have the same rank.
Thus the value of the variable b from your example is interpreted as an unsigned value.
If to use the correct conversion specifier %u for the expression ( a + b ) in a function call of printf then you might get
#include <stdio.h>
int main(void)
{
unsigned int a = 6;
int b = -20;
printf( "a + b = %u\n", a + b );
return 0;
}
a + b = 4294967282
You compare signed with unsigned. As all values of unsigned cannot be accommodated in the signed so the the signed value is converted to unsigned and is much larger than 6

Can anyone please explain the output of below C program? [duplicate]

This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 7 years ago.
#include<stdio.h>
int main(){
unsigned int a = 6;
int b = -20;
( a+b > 6 ) ? puts( "a") : puts( "b");// if a+b > 6 then a else b
}
I presumed that the output should be "b" but it wasn't.
output: a
C99 standard, section 6.3.1.8
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.
So here b(value= -20) is converted to unsigned type (a large value), it will be equivalent to -
( a+(unsigned int)b > 6 )
Therefore , output is a not b
The type of the expression a + b is unsigned int, which is the result of arithmetic conversions that are applied whenever the arithmetic operators are used on operands of different types. Each operand is first converted to this common type, and the result of converting -20 to an unsigned int is a very large value.

Resources