This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
Closed 4 years ago.
int main(void)
{
unsigned int y = 10;
int x = – 4;
if (x > y)
Printf("x is greater");
else
Printf("y is greater");
getch();
return (0);
}
Output: x is greater
I thought the output would be y is greater since it is unsigned. What's the reason behind this?
Because the int value is promoted to an unsigned int. specifically 0xFFFFFFFC on a 32-bit machine, which as an unsigned int is 4294967292, considerably larger than 10
C99 6.3.1.1-p2
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
To perform the conversion:
C99 6.3.1.3-p2
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.
Which basically means "add UINT_MAX+1" (as I read it, anyway).
Regarding why the promotion was to the unsigned int side; precedence:
C99 6.3.1.8-p1
...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.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Which tells me int vs. unsigned char should work as expected.
Test
int main()
{
int x = -4;
unsigned int y = 10;
unsigned char z = 10;
if (x > y)
printf("x>y\n");
else
printf("x<y\n");
if (x > z)
printf("x>z\n");
else
printf("x<z\n");
return 0;
}
Output
x>y
x<z
Well look at that.
A comparison between a signed and an unsigned value will be made in "unsigned space". I.e., the signed value will be converted to unsigned by adding UINT_MAX + 1. In implementation using the 2-complement for negative values, no special handling of the values is required under the hood.
In this example, the -4 is turned into a 0x100000000-4 = 0xFFFFFFFC which is clearly > 10.
When you compare two values in C, they both must be of the same type. In this case (int and unsigned int) the int value will be converted to an unsigned int first.
Second, unsigned integer arithmetic in C is done modulo the maximum value of that type + 1 (that is, it "loops around" so UINT_MAX + 1 is 0 again and vice versa). Therefore converting negative values to unsigned results in very large numbers.
The relevant section in the standard says:
6.3.1.3 Signed and unsigned integers
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.
When you compare an int and an unsigned int the int is converted to unsigned int.
The convertion of an int to an unsigned int is done by adding UINT_MAX+1 (note that your int is negative). So actually you are comparing:
if (-3 + UINT_MAX > 10) //Since -4 is converted to UINT_MAX+1-4
Which is true.
The first bit of an int value is used to define if it's a positive or a negative one. (1 = negative, 0 positive)
Your both variable are cast into unsigned int before comparison where the 1 in the first bit will be interpreted as part of your number.
this code should work fine :
int main(void)
{
unsigned int y = 10;
int x = – 4;
if (x > (int) y)
Printf("x is greater");
else
Printf ("y is greater");
getch ( );
return (0);
}
int x=-4 (2's complement of 4 is 1111 1100 =252) and unsigned int y=10 is(0000 1010 =10) so 252 >10 so -4 is greater than 10.
Related
I'm trying to subtract two unsigned ints and compare the result to a signed int (or a literal). When using unsigned int types the behavior is as expected. When using uint16_t (from stdint.h) types the behavior is not what I would expect. The comparison was done using gcc 4.5.
Given the following code:
unsigned int a;
unsigned int b;
a = 5;
b = 20;
printf("%u\n", (a-b) < 10);
The output is 0, which is what I expected. Both a and b are unsigned, and b is larger than a, so the result is a large unsigned number which is greater than 10. Now if I change a and b to type uint16_t:
uint16_t a;
uint16_t b;
a = 5;
b = 20;
printf("%u\n", (a-b) < 10);
The output is 1. Why is this? Is the result of subtraction between two uint16_t types stored in an int in gcc? If I change the 10 to 10U the output is again 0, which seems to support this (if the subtraction result is stored as an int and the comparison is made against an unsigned int than the subtraction results will be converted to an unsigned int).
Because calculations are not done with types below int / unsigned int (char, short, unsigned short etc; but not long, unsigned long etc), but they are first promoted to one of int or unsigned int. "uint16_t" is possibly "unsigned short" on your implementation, which is promoted to "int" on your implementation. So the result of that calculation then is "-15", which is smaller than 10.
On older implementations that calculate with 16bit, "int" may not be able to represent all values of "unsigned short" because both have the same bitwidth. Such implementations must promote "unsigned short" to "unsigned int". On such implementations, your comparison results in "0".
Before both the - and < operations are performed, a set of conversions called the usual arithmetic conversions are applied to convert the operands to a common type. As part of this process, the integer promotions are applied, which promote types narrower than int or unsigned int to one of those two types.
In the first case, the types of a and b are unsigned int, so no change of types occurs due to the - operator - the result is an unsigned int with the large positive value UINT_MAX - 14. Then, because int and unsigned int have the same rank, the value 10 with type int is converted to unsigned int, and the comparison is then performed resulting in the value 0.
In the second case, it is apparent that on your implementation the type int can hold all the values of the type uint16_t. This means that when the integer promotions are applied, the values of a and b are promoted to type int. The subtraction is performed, resulting in the value -15 with type int. Both operands to the < are already int, so no conversions are performed; the result of the < is 1.
When you use a 10U in the latter case, the result of a - b is still -15 with type int. Now, however, the usual arithmetic conversions cause this value to be converted to unsigned int (just as the 10 was in the first example), which results in the value UINT_MAX - 14; the result of the < is 0.
[...] Otherwise, the integer promotions are performed on both operands. (6.3.1.8)
When uin16_t is a sub-range of int, (a-b) < 10 is performed using int math.
Use an unsigned constant to gently nudge the left side to unsigned math.
// printf("%u\n", (a-b) < 10);
printf("%d\n", (0u + a - b) < 10); // Using %d as the result of a compare is int.
// or to quiet some picky warnings
printf("%d\n", (0u + a - b) < 10u);
(a - b) < 10u also works with this simple code, yet the idea I am suggesting to to perform both sides as unsigned math as that may be needed with more complex code.
#include <stdio.h>
int main()
{
unsigned int x =1;
signed char y = -1;
unsigned int sum = x + y;
printf("%u", sum);
}
In the above program I expected signed char to be upcasted to unsigned int and hence sum to be x + y = 1 + 2^32 -1 = 2^32. But surprisingly it prints 0.
Previously, I had tried printing (x>y) and got false (0) as the output. I can't figure out what's going on here, could someone explain how does one about casting in such cases?
It shouldn't be surprising that computing 232 as an unsigned int results in 0. On a machine with 32-bit ints, UINT_MAX is 232−1 and 232 is out of range. As with any other unsigned arithmetic, the out-of-range value is reduced modulus UINT_MAX + 1 (i.e., 232), resulting in 0.
Specifically, in the evaluation of x + y:
First, y is converted to a (signed) int, as per the "integer promotions". This doesn't change the value of y; it is still -1.
Then, as per the "usual arithmetic conversions", since unsigned int and int have the same rank, y is converted to unsigned int, making its value 232 − 1.
Finally, the addition is computed using unsigned int arithmetic. That results in 0, as above.
This exact same sequence is followed for the evaluation of x > y. Since y has been converted to an unsigned int before the comparison is evaluated, the result is (perhaps unexpectedly) false. That's why some compilers will warn about comparison between signed and unsigned values.
Also, the type of the variable being assigned to does not alter the computation. Only when the result is computed is any consideration taken of what will be done with the result. If, for example, sum had been declared unsigned long long int, the computation would be done identically and sum would still be 0. For the extra precision to be useful, you would have to first cast y to unsigned int manually, and then ensure that the addition was computed with extra precision by manually casting one of the arguments to +:
unsigned int y_as_int = y;
unsigned long long sum = x + (unsigned long long)y_as_int;
This question already has answers here:
Why is an unsigned int 1 lower than a char y -1?
(2 answers)
Closed 3 years ago.
#include<stdio.h>
void main()
{
unsigned x = 1;
signed char y = -1;
if(x > y)
printf("x > y");
else if(x == y)
printf("x == y");
else
printf("x < y");
printf("\n");
printf("%d",(signed char)x);
printf("\n");
printf("%d",(unsigned int)y);
}
OUTPUT:
x < y
1
-1
I expected the output to be x == y as during comparison signed character is supposed to be converted to unsigned int?
Please explain me how x < y...
I expected the output to be x == y as during comparison signed character is supposed to be converted to unsigned int?
Well, you're halfway there.
When a value of -1 is converted (promoted, actually) to unsigned int, the representation produces the biggest possible value representable by the type. Hence, the promoted value becomes greater than x which is 1.
Quoting C11, chapter §6.3.1.8, 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.
To clarify, the promotion does not mean, it removes the signedness. The operand (value), with the sign, is treated as the promoted type. The value is determined from the bit representation. The details: chapter §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.
To add to above, the usage
printf("%d",(signed char)x);
and
printf("%d",(unsigned int)y);
are no good. %d expects a signed integer type (int) as argument.
If you want to print a signed char value, use %hhd
If you want to print an unsigned int, use %u
What is the bit representation of unsigned int x =-1;
Can we assign unsigned int with a negative integer?
#include<stdio.h>
int main(){
unsigned int x = -1;
int y = ~0;
if(x == y)
printf("same");
else
printf("not same");
return 0;
}
output :
same
and how is it possible, x being unsigned
#include<stdio.h>
int main()
{
unsigned int x = -4;
if (x == -4)
printf("true");
else
printf("FALSE");
}
output:
true
When an unsigned int value is compared to int value, the int value is implicitly converted to unsigned int type. The result of that conversion is congruent to the original value modulo 2N, where N is the number of value-forming bits in unsigned int. This modulo equals to UINT_MAX + 1.
For this reason initialization
unsigned int x = -1;
initializes x with some unsigned value congruent to -1 modulo UINT_MAX + 1. Incidentally, this is nothing else than UINT_MAX. This value has 1 in each value-forming bit of unsigned int object. It works that way with any unsigned type.
Expression ~0 is evaluated in the domain of signed int type, and then y is implicitly converted to unsigned int in x == y comparison. Apparently, on your platform the conversion produces the same unsigned int value with all value-forming bits set to 1. Hence the equality.
Initialization
unsigned int x = -4;
initializes x with some unsigned value congruent to -4 modulo UINT_MAX + 1. In comparison x == -4 the right-hand side is converted to unsigned type by the very same rules. Hence the equality.
and how is it possible, x being unsigned
This is the "usual arithmetic conversions" -- the operands on either side of the == have different types, so they must be converted to the same types first. The rule for types of int or larger when the types differ only by signedness is that unsigned wins, so this converts -4 into the unsigned equivalent value, just as it did when you assigned -4 into an unsigned integer.
For more information, see How do promotion rules work when the signedness on either side of a binary operator differ?
in int negative integers are stored in 2's complement.
suppose we have a data type say of 4 bits.
the first bit 0 represents that the integer is positive otherwise negative, so its max value is 0111 i.e 7
so 1 can be written as 0001
and for -1, it has to be written in its 2's complement, since it is negative ;
-1 = ~(0001) +1 = (1110) + 1 = 1111;
so making this data type unsigned reads -1 as 15.
Suppose the following:
unsigned char foo = 3;
unsigned char bar = 5;
unsigned int shmoo = foo + bar;
Are foo and bar values guaranteed to be promoted to int values for the evaluation of the expression foo + bar -- or are implementations allowed to promote them to unsigned int?
In section 6.2.5 paragraph 8:
For any two integer types with the same signedness and different integer conversion rank
(see 6.3.1.1), the range of values of the type with smaller integer conversion rank is a
subrange of the values of the other type.
In section 6.2.5 paragraph 9:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.
The guarantee that an integer type with smaller integer conversion rank has a range of values that is a subrange of the values of the other type seems dependent on the signedness of the integer type.
signed char corresponds to signed int
unsigned char corresponds to unsigned int
Does this mean that the value of an unsigned char is only guaranteed to be in the subrange of unsigned int and not necessarily int? If so, does that imply that an implementation could theoretically have an unsigned char value which is not in the subrange of an int?
are implementations allowed to promote them to unsigned int?
Implementations will promote to unsigned int if not all unsigned char values are representable in an int (as ruled by 6.2.5p9 in C99). See below for implementation examples.
If so, does that imply that an implementation could theoretically have an unsigned char value which is not in the subrange of an int?
Yes, example: DSP cpu with CHAR_BIT 16 or 32.
For example, TI C compiler for TMS320C55x: CHAR_BIT is 16 and UCHAR_MAX 65535, UINT_MAX 65535 but INT_MAX 32767.
http://focus.ti.com/lit/ug/spru281f/spru281f.pdf
I ran across this yesterday - hope that my answer is on topic.
uint8_t x = 10;
uint8_t y = 250;
if (x - y > 0) {
// never happens
}
if (x - y < 0U) {
// always happens
}
To my eyes at least it was appearing as though values x and y were being unexpectedly promoted, when in fact is was their result that was promoted.