Why is signed char not getting upcasted to unsigned int here? - c

#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;

Related

16-bit unsigned variable casting to generate correct result [duplicate]

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.

Unexpected results on difference of unsigned ints

I was surprised that this function produces different values for dif1 and dif2
void test()
{
unsigned int x = 0, y = 1;
long long dif1 = x - y;
long long dif2 = (int)(x - y);
printf("dif = %lld %lld",dif1,dif2);
}
Is that correct behavior? In the dif1 computation it first promotes the 32-bit unsigned difference to a 64-bit unsigned value, then adds the sign. Is that standard behavior, not specified by the language, or a compiler bug? Is the second form guaranteed to produce -1, or up to the compiler implementation? I guess the safest construction is:
long long dif3 = (long long)x - (long long)y;
The first one is definitely defined, if we assume that long long is wider than unsigned int. If it isn't, then the assignment gives the same problem as the second part of the answer.
long long dif1 = x - y;
Unsigned integers will wrap and you get a maximum value that can be stored in an unsigned int.
6.2.5 p9: A computation involving unsigned operands can never 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 type.
As for the second
long long dif2 = (int)(x - y);
it is implementation defined:
6.3.1.3 p3: 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.
In this case a maximum value for unsigned int cannot be represented in an int ant the above rule is in effect.
There's nothing surprising about it.
unsigned int x = 0, y = 1;
long long dif1 = x - y;
long long dif2 = (int)(x - y);
The second has one difference to the first:
A cast to signed.
The cast is defined to be value-preserving if possible (Not possible as UINT_MAX is bigger than INT_MAX), and otherwise implementation-defined (though it is allowed to trap).
If we have 2s-complement on cast (likely), the result of the cast is -1.
Next, we have an assignment to a wider signed type, which is always value-preserving.

Why is a negative int greater than unsigned int? [duplicate]

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.

Implicit conversion of unsigned char (arithmetic conversion vs. assignment)

In am working on something and came across code similar to the following:
#define MODULUS(a,b) ((a) >= 0 ? (a)%(b) : (b)-(-a)%(b))
unsigned char w;
unsigned char x;
unsigned char y;
char z;
/* Code that assigns values to w,x and y. All values assigned
could have been represented by a signed char. */
z = MODULUS((x - y), w);
It is my understanding that the arithmetic (x - y) will be accomplished prior to any type conversion and the macro will always evaluate to (a)%(b) -- as the result will be an unsigned char which is always greater than or equal to zero. However, the code functions as intended and I think that my understanding is flawed. So...
My questions are these:
Does an implicit type conversion to signed char occur before the expression is evaluated?
Is there a situation (for example, if the unsigned values were large enough that they could not be represented by a signed value) where the above code would not work?
Does an implicit type conversion to signed char occur before the expression is evaluated?
No, a conversion to int occurs before the expression x - y is evaluated¹. Thus the result can be negative.
Is there a situation (for example, if the unsigned values were large enough that they could not be represented by a signed value) where the above code would not work?
If sizeof int == 1, the integer promotion would promote the unsigned chars to unsigned ints, and that could produce wrong results because before the modulus by w, a modulus by UINT_MAX + 1 is performed due to unsigned arithmetic.
¹ The default integer promotion.

What type-conversions are happening?

#include "stdio.h"
int main()
{
int x = -13701;
unsigned int y = 3;
signed short z = x / y;
printf("z = %d\n", z);
return 0;
}
I would expect the answer to be -4567. I am getting "z = 17278".
Why does a promotion of these numbers result in 17278?
I executed this in Code Pad.
The hidden type conversions are:
signed short z = (signed short) (((unsigned int) x) / y);
When you mix signed and unsigned types the unsigned ones win. x is converted to unsigned int, divided by 3, and then that result is down-converted to (signed) short. With 32-bit integers:
(unsigned) -13701 == (unsigned) 0xFFFFCA7B // Bit pattern
(unsigned) 0xFFFFCA7B == (unsigned) 4294953595 // Re-interpret as unsigned
(unsigned) 4294953595 / 3 == (unsigned) 1431651198 // Divide by 3
(unsigned) 1431651198 == (unsigned) 0x5555437E // Bit pattern of that result
(short) 0x5555437E == (short) 0x437E // Strip high 16 bits
(short) 0x437E == (short) 17278 // Re-interpret as short
By the way, the signed keyword is unnecessary. signed short is a longer way of saying short. The only type that needs an explicit signed is char. char can be signed or unsigned depending on the platform; all other types are always signed by default.
Short answer: the division first promotes x to unsigned. Only then the result is cast back to a signed short.
Long answer: read this SO thread.
The problems comes from the unsigned int y. Indeed, x/y becomes unsigned. It works with :
#include "stdio.h"
int main()
{
int x = -13701;
signed int y = 3;
signed short z = x / y;
printf("z = %d\n", z);
return 0;
}
Every time you mix "large" signed and unsigned values in additive and multiplicative arithmetic operations, unsigned type "wins" and the evaluation is performed in the domain of the unsigned type ("large" means int and larger). If your original signed value was negative, it first will be converted to positive unsigned value in accordance with the rules of signed-to-unsigned conversions. In your case -13701 will turn into UINT_MAX + 1 - 13701 and the result will be used as the dividend.
Note that the result of signed-to-unsigned conversion on a typical 32-bit int platform will result in unsigned value 4294953595. After division by 3 you'll get 1431651198. This value is too large to be forced into a short object on a platform with 16-bit short type. An attempt to do that results in implementation-defined behavior. So, if the properties of your platform are the same as in my assumptions, then your code produces implementation-defined behavior. Formally speaking, the "meaningless" 17278 value you are getting is nothing more than a specific manifestation of that implementation-defined behavior. It is possible, that if you compiled your code with overflow checking enabled (if your compiler supports them), it would trap on the assignment.

Resources