Why is an unsigned int 1 lower than a char y -1? - c

main() {
unsigned x = 1;
char y = -1;
if (x > y)
printf("x>y");
else
printf("x<=y");
}
I expected x>y,
but I had to change unsigned int to signed int to get the expected result.

If char is equivalent to signed char:
char is promoted to int (Integer Promotions, ISO C99 §6.3.1.1 ¶2)
Since int and unsigned int have the same rank, int is converted to unsigned int (Arithmetic Conversions, ISO C99 §6.3.1.8)
If char is equivalent to unsigned char:
char may be promoted to either int or unsigned int:
If int can represent all unsigned char values (typically because sizeof(int) > sizeof(char)), char is converted to int.
Otherwise (typically because sizeof(char)==sizeof(int)), char is converted to unsigned.
Now we have one operand that is either int or unsigned int, and another that is unsigned int. The first operand is converted to unsigned int.
Integer promotions:
An expression of a type of lower rank that int is converted to int if int can hold all of the values of the original type, to unsigned int otherwise.
Arithmetic conversions:
Try to convert to the larger type. When there is conflict between signed and unsigned, if the larger (including the case where the two types have the same rank) type is unsigned, go with unsigned. Otherwise, go with signed only in the case it can represent all the values of both types.
Conversions to integer types(ISO C99 §6.3.1.3):
Conversion of an out-of-range value to an unsigned integer type is done via wrap-around (modular arithmetic).
Conversion of an out-of-range value to a signed integer type is implementation defined, and can raise a signal (such as SIGFPE).

When using signed and unsigned in single operation the signed got promoted to unsigned by C's automatic type conversion. If the bit patter of -1 is considered an unsigned number then it is a very very high value. So x > y is false.

Related

Ranking sometimes fails when int and long have the same width in C

Let platform 1 have the width of int 4 bytes and the width of long 8 bytes.
Let platform 2 have the width of int 4 bytes and the width of long be the same as the width of int.
Then given:
unsigned int x = 2;
long signed int y = 3;
func(x * y);
When ran on platform 1, the effective type of the first argument of func is long signed int. This is as expected. It follows § 6.3.1.1.1.4 that the unsigned int type has the same rank as signed int. It also then follows that signed int type has a lower rank than long signed int according to § 6.3.1.1.1.3. This then triggers the result of the multiplication to be converted to long signed int type, following § 6.3.1.8.1.4.4. Great!
When ran on platform 2, the result of the multiplication is long unsigned int. Why?
Background
Section § 6.3.1.1, subsection 1, point 3 in the C99 standard says:
The rank for long long int shall be greater than the rank of long int,
which shall be greater than the rank of int, which shall be greater
that the rank of short int, which shall be greater than the rank of
signed char.
One thing this suggests is that long int has a higher rank than int.
Also, point 4 in the same paragraph in the C99 standard says:
The rank of any unsigned integer type shall equal the rank of the
corresponding signed integer type, if any.
A couple of things from here are that the unsigned int type has the same rank as the signed int type. Similarly, the long unsigned int type has the same rank as long signed int.
Lastly, in section § 6.3.1.8, subsection 1, point 4.3 says:
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.
And point 4.4:
Otherwise, if the type of the operand with signed integer type can
represent all 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.
Test code
#include <stdio.h>
#define func(x) _Generic((x), long unsigned int: func_longunsignedint, long signed int: func_longsignedint, signed int: func_signedint, unsigned int: func_unsignedint)(x);
void func_longunsignedint (long unsigned int x)
{
printf("%s\t%lu\n", __func__, x);
}
void func_longsignedint (long signed int x)
{
printf("%s\t%ld\n", __func__, x);
}
void func_signedint (signed int x)
{
printf("%s\t%d\n", __func__, x);
}
void func_unsignedint (unsigned int x)
{
printf("%s\t%u\n", __func__, x);
}
int main(void)
{
printf("int width %d\n", sizeof(int));
printf("long width %d\n", sizeof(long));
unsigned int x = 2;
long signed int y = -3;
func(x * y);
}
For platform 1, compile with gcc -m64 to hopefully force long to the 8 bytes and int be 4 bytes.
For platform 2, compile with gcc -m32 to hopefully force long to the 4 bytes and int be 4 bytes.
We take unsigned and long signed int and follow C11 6.3.1.8:
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.
On 64-bit platform #1 type long signed int can represent all values of unsigned, because it has 64 bits vs unsigned has 32bits. So the unsigned is converted to long signed int.
On 32-bit platform #2 type long signed int can't represent all values of unsigned (UINT_MAX=2^32 but LONG_MAX=2^31-1). So we continue...
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
...so on platform #2 the "unsigned integer type corresponding to the type of signed integer type" - this is long signed int + unsigned = long unsigned int. So on platform #2 both operands are converted to long unsigned int, thus the result your are seeing.

Unsigned char overflow with subtraction

I am trying to work out how unsigned overflow works with subtraction, so I wrote the following test to try it out:
#include<stdio.h>
#include<stdlib.h>
unsigned char minWrap(unsigned char a, unsigned char b) {
return a > b ? a - b : a + (0xff - b) + 1;
}
int main(int argc, char *argv[]) {
unsigned char a = 0x01, b = 0xff;
unsigned char c = a - b;
printf("0x%02x 0x%02x 0x%02x\n", a-b, c, minWrap(a,b));
return EXIT_SUCCESS;
}
Which gave as output:
0xffffff02 0x02 0x02
I would have expected the output to be the same three times. My question is: is it always safe to add/subtract unsigned chars and expect them to wrap around at 0xff?
Or more general, is it safe to compute with uintN_t and expect the result to be modulo 2^N?
is it always safe to add/subtract unsigned chars and expect them to wrap around at 0xff?
No. In C, objects of type char go through the usual integer promotions. So if the range of char fits in int (usual), it is converts to int, else unsigned.
a - b --> 0x01 - 0xFF --> 1 - 255 --> -254.
The below is undefined behavior as %x does not match an int and the value of -254 is not in the unsigned range (See #EOF comment). A typical behavior is a conversion to unsigned
printf("0x%02x\n", a-b);
// 0xffffff02
it safe to compute with uintN_t and expect the result to be modulo 2^N?
Yes. But be sure to make the result of type uintN_t and avoid unexpected usual integer promotions.
#include <inttypes.h>
uint8_t a = 0x01, b = 0xff;
uint8_t diff = a - b;
printf("0x%02x\n", (unsigned) diff);
printf("0x%02" PRTx8 "\n", diff);
a-b in the printf line is evaluated after a and b are promoted to int. Also, the value is being treated as an unsigned int due to the use of %x in the format specifier by your run time environment.
It's equivalent to:
int a1 = a;
int b1 = b;
int x = a1 - b1;
printf("0x%02x 0x%02x 0x%02x\n", x, c, minWrap(a,b));
Section 6.3.1.8 Usual arithmetic conversions of the C99 standard has more details.
In theory use of an int when an unsigned int is expected in printf is cause for undefined behavior. A lenient run time environment, like you have, treats the int as an unsigned int and proceeds to print the value.
From 6.3.1.8/1 of the standard concerning integer conversions:
The integer promotions are performed on both operands. Then the following rules are applied to the promoted operands
If both operands have the same type, then no further conversion is
needed.
Otherwise, if both operands have signed integer types or both have
unsigned integer types, the operand with the type of lesser integer
conversion rank is converted to the type of the operand with greater
rank.
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.
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
In this case, the wrap-around is well defined. In the expression a-b, because both operands are of type unsigned char, they are first promoted to int and the operation is performed. If this value was assigned to an unsigned char, it would be properly truncated. However, you're passing this value to printf with a %x format specifier which expects an unsigned int. To display it correctly, use %hhx which expects an unsigned char.

Is unsigned char always promoted to int?

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.

Unsigned integer and unsigned char holding same value yet behaving differently why?

Why is it so that
unsigned char k=-1
if(k==-1)
is false
unsigned int k=-1
if(k==-1)
is true
For the purpose of demonstration let's assume 8-bit chars and 32-bit ints.
unsigned char k=-1;
k is assigned the value 255.
if(k==-1)
The left-hand side of the == operator is an unsigned char. The right-hand side is an int. Since all possible values of an unsigned char can fit inside an int, the left-hand side is converted to an int (this is performed due the the integer promotions, quoted below). This results in the comparison (255 == -1), which is false.
unsigned int k=-1
k is assigned the value 4294967295
if(k==-1)
This time, the left-hand side (an unsigned int) cannot fit within an int. The standard says that in this case, both values are converted to an unsigned int. So this results in the comparison (4294967295 == 4294967295), which is true.
The relevant quotes from the standard:
Integer promotions: (C99, 6.3.1.1p2)
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.
Usual arithmetic conversions: (6.3.1.8).
[For integral operands, ] the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
- If both operands have the same type, then no further conversion is needed.
...
- 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.
...
§6.3.1.1p2 of the C11 standard draft (n1570.pdf):
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.58) All other types are unchanged by the integer
promotions.
In your second case, an int can't represent unsigned int k because that's out of range. Both operands end up being converted to unsigned int and compare equal.
As there is no sign extension in unsigned promotion the result is defferent.
unsigned char k is promoted from unsigned char (value=255) to int (value=255).
In the case of unsigned int k, -1 is promoted from int (value=-1) to unsigned int (value=2^32-1).
unsigned char k = -1;
-1 is an integer literal of type int, which is equivalent to signed int. When you assign a large signed integer to a smaller unsigned type, the result will get truncated in undefined ways, the C standard does not guarantee what will happen.
In the real world outside the C standard, this is what's most likely (assuming 32-bit CPU, two's complement):
-1 is 0xFFFFFFFF. The least significant byte of 0xFFFFFFFF will get assigned to k. k == 255. Try to print it using printf("%u") and see for yourself.
In the case of unsigned int k=-1, -1 is still a signed int. But it gets implicitly (silently) promoted to an unsigned int when it is stored in k. When you later compare k == -1, the right side -1 will once more get promoted to an unsigned type, and will compare equal with the data stored in k.

unsigned int and signed char comparison

I am trying to compare an unsigned int with a signed char like this:
int main(){
unsigned int x = 9;
signed char y = -1;
x < y ? printf("s") : printf("g");
return 0;
}
I was expecting the o/p to be "g". Instead, its "s". What kind of conversion is done here?
Section 6.3.1.8, Usual arithmetic conversions, of C99 details implicit integer conversions.
If both operands have the same type, then no further conversion is needed.
That doesn't count since they're different types.
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
That doesn't count since one is signed, the other unsigned.
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.
Bingo. x has a higher rank than y so y is promoted to unsigned int. That means that it morphs from -1 into UINT_MAX, substantially larger than 9.
The rest of the rules don't apply since we have found our match but I'll include them for completeness:
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.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
The ranks relevant to this question are shown below. All ranks are detailed in C99, section 6.3.1.1, Boolean, character, and integers so you can refer to that for further details.
The rank of long long int shall be greater than the rank of long int, which
shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.
The rank of char shall equal the rank of signed char and unsigned char.
My guess is y is promoted to unsigned int which becomes a big value (due to wrapping). Hence the condition is satisfied.
Ran the following code:
int main(){
unsigned int x = 9;
signed char y = -1;
printf("%u\n", (unsigned int)y);
x < (unsigned int)y ? printf("s") : printf("g");
return 0;
}
The output is:
4294967295
s
After a casting, y takes a very large value. That is why output is s.
The char is promoted to unsigned int, with a value of MAX_UINT, which is greater than 9.

Resources