I'm confused by the issue stated in the title. I've been told that in expressions involving both types of variables, signed are converted to/interpreted as unsigned. However, as the following code snippet shows, that isn't always the case.
Code:
unsigned int x = 1;
int y = -20;
printf("Right shift = %x, %d\n", y>>x, y>>x);
printf("If = %x, %d\n", y < x, y < x);
Result:
Right shift = fffffff6, -10
If = 0, 0
The if statement returns the expected 0, -20 being cast to a very large unsigned integer, but the shift expression returns -10, making it evident that arithmetic and not logical shift has taken place. The x has been interpreted as signed rather than the y being interpreted as unsigned.
Could anyone clear this up for me?
In this expression
y>>x
(the C Standard, 6.5.7 Bitwise shift operators)
3 The integer promotions are performed on each of the operands.
That means that as y has the type int and x has the type unsigned int neither conversion (promotion) occurs. And
...The type of the result is that of the promoted left operand.
So the result of the expression has the type int - the type of the operand y. As y has a negative value then
...IfE1 has a signed type and a negative value, the resulting value is
implementation-defined
As for this expression
y < x
then there is used the usual arithmetic conversions. The boths operands have the same rank so the operand y of the type int is converted to the type unsigned int and its binary representation as an object of the type unsigned int is greater than the binary representation of the operand x.
From the C Standard *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.
Related
int main()
{
printf("Hello World\n");
int x = -10;
unsigned y = 25;
float z = x*y;
printf("x=%d,y=%u,z=%f\n",x,y,z);
return 0;
}
When I run the above code, I get the following output:
Hello World
x=-10,y=25,z=4294967046.000000
My question is:
For the second printf, I would have expected z=(float) ( (unsigned)(-10)*25 ) = (float) (4294967286 x 25) = (float) 107374182150, what am I missing here?
Here's what's happening. As per C11 6.3.1.8 Usual arithmetic conversions (the "otherwise" comes into play here since previous paragraphs discuss what happens when either type is already floating point):
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.
This means your signed value of -10 becomes an unsigned value of 0xffff'fff6, or 4,294,967,286. Multiplying that by 25 gives 107,374,182,150 or 0x18'ffff'ff06 (which is the result you want).
However, at this point, no float calculations have been done, the multiplication is a pure integer calculation and the resultant value will be an integer. And that, combined with the fact your unsigned integers are 32 bits long, means it gets truncated to 0xffff'ff06, or 4,294,967,046.
Then you put that into the float.
To fix this to match your expected results, you should change he expression to force this:
float z = 1.0f * (unsigned)x * y;
This changes the int * unsigned-int calculation into a float * unsigned-int * unsigned-int one. The unsigned cast first ensures x will be converted to the equivalent unsigned value and the multiplication by 1.0f ensures the multiplication are done in the float arena to avoid integer truncation.
Following on from the correct answer from #paxdiablo, the starting point for the result is due to unsigned having a rank equal to the rank of the int, e,g,
The rank of any unsigned integer type shall equal the rank of the
corresponding signed integer type, if any. C11 Standard - 6.3.1
Arithmetic
operands(p1)
This comes into play with the integer conversion cited in #paxdiablo's answer:
6.3.1.8 Usual arithmetic conversions
Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
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.
The problem is that -10 (negative values) are stored (in almost all computers) in two-complement. In two's complement the value for -10 takes the bitwise NOT of 10 and adds 1 (so in binary 00001010 become 11110110 sign extended to 32-bits). That is:
11111111111111111111111111110110
For which the unsigned values is 4294967286. When multiplied by 25, it exceeds the range of unsigned so the value is reduced modulo until it fits within the range of unsigned resulting in 4294967046. 6.2.5 Types(p9).
What Am I Missing?
The part that is missing is understanding the result of unsigned multiplication is being assigned as a float value. The intermediate result from x * y is unsigned. float f = x * y; is just an assignment of the result to a float.
What you want is for the intermediate calculation to be done as a float, so cast one of the operands (not the result) to float, e.g.
float f = (float)x * y
It does not matter which of the two values is cast to float, the following would be just fine:
float f = x * (float)y;
Now the result will be -250.
I don't understand why the variable have value of 0, it should be 23?
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int array[] = {23, 34, 12, 17, 204, 99, 16};
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int main(){
int d = -1, x = 0;
if (d <= TOTAL_ELEMENTS - 2)
x = array[d + 1];
printf("x= %d \n", x);
return 0;
}
This is due to the usual arithmetic conversions.
When the operands of a operator involve both a signed integer and an unsigned integer, and the unsigned type is at least as large as the signed type, the signed value is converted to an unsigned value. When the signed value is negative, it gets converted to a large positive value.
The rules for integer conversions are spelled out in section 6.3.1.8p1 of the C standard:
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 expression:
(d <= TOTAL_ELEMENTS - 2)
Which expands to:
(d <= (sizeof(array) / sizeof(array[0])) - 2)
The sizeof operator evaluates to a value of type size_t which is unsigned. So the types of the operands look like this:
(int <= ((size_t / size_t) - int)
Both operands of the / operator are of type size_t so the result of that operation is of type size_t. Then the right operand of - is converted to type size_t. Since the value 2 fits in that type the value doesn't change.
Now we have the <= operator with an int on one size and a size_t on the other. The left operand is converted from int to size_t, however the value -1 doesn't fit in that type so it is converted. The converted value is actually the largest possible value for a size_t which is therefore greater than the value on the right side, making the result of <= false.
To fix this, you'll want to cast the unsigned value on the right to signed to prevent the left side from being converted:
if (d <= (int)(TOTAL_ELEMENTS - 2))
This question already has answers here:
Why is −1 > sizeof(int)?
(4 answers)
Closed 6 years ago.
The following program compiles successfully but when i ran it ,it prints nothing when i initialize the for loop with -1 but when i initialize for loop with 0 it successfully traverse all the array.I want to ask that can we don't traverse the array when we initialize the for loop with negative value??
#include <stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23, 34, 12, 17, 204, 99, 16};
int main(void) {
int d;
//printf("%d",TOTAL_ELEMENTS);
for (d = -1; d <= (TOTAL_ELEMENTS - 2); d++)
{
printf("%d ",d);
printf("%d\n", array[d+1]);
}
return 0;
}
The result of sizeof operator is of type size_t, which is an unsigned type.
As a result, the type of TOTAL_ELEMENTS is also unsigned. When -1 is compared with it, it's converted to a big unsigned number. That's why d <= (TOTAL_ELEMENTS - 2) is false.
Here d <= (TOTAL_ELEMENTS - 2) operands are subject of usual arithmetic conversions (6.3.1.8). And actually integer promotions rules act in your case:
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.
According to what you've got your code falls into clause #3, then your signed -1 is converted via rule (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.
As result it becomes a very large unsigned value, that is surely greater than TOTAL_ELEMENTS - 2 and you'll never enter the loop.
This doesn't do what you think it does:
d <= (TOTAL_ELEMENTS - 2)
Instead, do this:
d <= int(TOTAL_ELEMENTS - 2)
Otherwise you've got a signed-vs-unsigned comparison, and your -1 becomes the largest possible size_t.
sizeof produces a result of size_t, which is unsigned. Compare a signed and unsigned type and you can only expect things to blow up.
To elaborate, when you try to use both signed and unsigned type in arithmatic operations, the signed type will be promoted to unsigned type, producing a huge number. Thus, the value of d , promoted to unsigned type, will fail to meet the condition d <= (TOTAL_ELEMENTS - 2);, hence the loop body will not execute.
For operators that expect operands of arithmetic type cause conversions. This pattern is called the usual arithmetic conversions. for this particular case, quoting the standard, chapter §6.3.1.8
Otherwise, the integer promotions are performed on both operands. Then the
following rules are applied to the promoted operands:
[...]
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, regarding the rank,
The rank of any unsigned integer type shall equal the rank of the corresponding
signed integer type, if any.
Also, for reference, quoting C11, chapter 7.19, (emphasis mine)
size_t
which is the unsigned integer type of the result of the sizeof operator;
Hint: Enable compiler warning and it will point to your mistake.
Sorry for bad English.
uint16_t a, c;
uint8_t b = 0xff;
a = b<<8;
c = b*10;
What is value of a and c we get? What is situation with arbitrary integer types?
uint16_t a, c;
uint8_t b = 0xff;
a = b<<8;
First, the integer promotions are performed on the arguments of <<. The constant 8 is an int and thus is not converted. Since the conversion rank of uint8_t is smaller than that of int, and all values of uint8_t are representable as ints, b is converted - preserving its value - to int. The resulting int value is then shifted left by eight bits.
If int is only 16 bits wide, the value 0xff * 2^8 is not representable as an int, and then the shift invokes undefined behaviour - 6.5.7 (4) in n1570 and C99:
If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
Otherwise, the result is 255*256 = 65280 = 0xFF00. Since that value is representable in the type of a, the conversion of the int result of the shift to uint16_t preserves the value; if the result were out of range (e.g. if the shift distance were 9 [and int wide enough]), it would be reduced modulo 2^16 to obtain a value in the range 0 to 2^16 - 1 of uint16_t.
c = b*10;
The usual arithmetic conversions are performed on the operands of *. Both operands have an integer type, thus first the integer promotions are performed. Since 10 is an int and all values of b's type are representable as an int, the integer promotions give both operands the same type, int, and the usual arithmetic conversions don't require any further conversions. The multiplication is done at type int, its result, 2550, is again representable in the type of c, so the conversion to uint16_t that is done before storing the value in c preserves the value.
What is situation with arbitrary integer types?
For <<:
integer promotions; values/expressions of an integer type whose conversion rank is less than or equal to that of int (and unsigned int) [integer types with width <= that of (unsigned) int], and bitfields of type _Bool, int, signed int and unsigned int are converted to int or unsigned int (int if that can represent all values of the original type, unsigned int otherwise).
If the (promoted) right operand (shift distance) is negative or greater than or equal to the width (number of value bits plus sign bits; there is either one sign bit or none) of the (promoted) left operand, the behaviour is undefined. If the value of the (promoted) left operand is negative, the behaviour is undefined. If the type of the (promoted) left operand is unsigned, the result is value * 2^distance, reduced modulo 2^width. If the type of the (promoted) left operand is signed and the value nonnegative, the result is value * 2^distance if that is representable in the type, the behaviour is undefined otherwise.
If no undefined behaviour occurred in 2., the result is converted to the type of the variable it is stored in.
If the target type is _Bool (or an alias thereof), a nonzero result is converted to 1, a zero result to 0, otherwise
If the result can be represented in the target type, its value is preserved, otherwise
If the target type is unsigned, the result is reduced modulo 2^width, otherwise
the result is converted in an implementation-defined manner or an implementation-defined signal is raised.
For *:
The usual arithmetic conversions are performed, so that both (converted) operands have the same type.
The multiplication is performed at the resulting type; if that is a signed integer type and the multiplication overflows, the behaviour is undefined.
The result is converted to the target type in the same manner as above.
That's how the abstract machine is defined, if the implementation can achieve the same results (where the behaviour is defined) in another manner, it can do as it pleases under the as-if rule.
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.