Normally, C requires that a binary operator's operands are promoted to the type of the higher-ranking operand. This can be exploited to avoid filling code with verbose casts, for example:
if (x-48U<10) ...
y = x+0ULL << 40;
etc.
However, I've found that, at least with gcc, this behavior does not work for bitshifts. I.e.
int x = 1;
unsigned long long y = x << 32ULL;
I would expect the type of the right-hand operand to cause the left-hand operand to be promoted to unsigned long long so that the shift succeeds. But instead, gcc prints a warning:
warning: left shift count >= width of type
Is gcc broken, or does the standard make some exception to the type promotion rules for bitshifts?
The so-called usual arithmetic conversions apply to many binary operators, but not all of them. For example they do not apply to the bit shift operators, &&, ||, comma operator, and assignment operators. This is the rule for the bit shift operators:
6.5.7 ... 3 Semantics ...
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
The trouble really is that promotion only works up to whatever your platform defines as an int. As some other answers have stated, the bit-shift operator will promote the left operand to an int. However, here an int is defined as a 32-bit value. The integer conversion will not promote to a long long (64-bit).
Related
This question already has answers here:
Implicit type promotion rules
(4 answers)
Closed 1 year ago.
I understand the difference between ++i and i++. I think this case is different,
I wrote this code,
u_int8_t a = 255;
main (){
printf("Num is %d\n", ++a ) ;
}
Which outputs
Num is 0
But this code,
u_int8_t a = 255;
main (){
printf("Num is %d\n", a+1 ) ;
}
Which outputs,
Num is 256
What is the reason for different outputs?
The expression ++a increments a in the context of its type (8-bit unsigned) so wraps around to zero.
The expression a + 1 adds the 8-bit unsigned a to the literal 1 (which is an int) and, when you do that with disparate types, the smaller of them is usually "upgraded" to the other before adding. Hence you're then adding the two int values, 255 and 1, with no wrapping.
This is known in C as the "usual arithmetic conversions" and the section of the standard that deals with it, C11 6.3.1.8, begins:
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.
After going through some floating point types, the following dictates how integral types are handled:
Otherwise, 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.
You may think that it's the fourth bullet point that comes into play here but, in actual fact, it's the preamble combined with the first bullet point. The "integer promotions", detailed in C11 6.3.1.1, will actually take integral types of lesser or equal rank to int and turn them into an int (assuming that can represent all values of the original type, otherwise it makes it unsigned int).
The other difference, of course, is that the first one modifies a but the second one does not.
You can squeeze the conversion between the variable and the unary prefix ++ operator:
printf("Num is %d\n", ++(int){a} ); // 256
The compound literal is needed; with only a cast there is a ++ needs lvalue error.
++a evaluates first to 0 and then gets converted . In a + 1 a gets converted first (and is not increased).
Normally, C requires that a binary operator's operands are promoted to the type of the higher-ranking operand. This can be exploited to avoid filling code with verbose casts, for example:
if (x-48U<10) ...
y = x+0ULL << 40;
etc.
However, I've found that, at least with gcc, this behavior does not work for bitshifts. I.e.
int x = 1;
unsigned long long y = x << 32ULL;
I would expect the type of the right-hand operand to cause the left-hand operand to be promoted to unsigned long long so that the shift succeeds. But instead, gcc prints a warning:
warning: left shift count >= width of type
Is gcc broken, or does the standard make some exception to the type promotion rules for bitshifts?
The so-called usual arithmetic conversions apply to many binary operators, but not all of them. For example they do not apply to the bit shift operators, &&, ||, comma operator, and assignment operators. This is the rule for the bit shift operators:
6.5.7 ... 3 Semantics ...
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
The trouble really is that promotion only works up to whatever your platform defines as an int. As some other answers have stated, the bit-shift operator will promote the left operand to an int. However, here an int is defined as a 32-bit value. The integer conversion will not promote to a long long (64-bit).
Even though I cast every right operand to unsigned long long, the warning persists. Shouldn't uint8_t << uint64_t have an implicit cast like this: (uint64_t) uint8_t << uint64_t?
This answer suggests that I can promote either of the operands and the whole expression will cast to unsigned long long, but it might be wrong.
bool dgBioReadU64LE(DgBioFile *file, uint64_t *x) {
uint8_t u[8];
if (!dgBioReadU8v(file, LEN(u), u)) return false;
*x = u[0]|(u[1]<<8ULL)|(u[2]<<16ULL)|(u[3]<<24ULL)|(u[4]<<32ULL)|(u[5]<<40ULL)|(u[6]<<48ULL)|(u[7]<<56ULL);
return true;
}
bool dgBioReadU64BE(DgBioFile *file, uint64_t *x) {
uint8_t u[8];
if (!dgBioReadU8v(file, LEN(u), u)) return false;
*x = u[7]|(u[6]<<8ULL)|(u[5]<<16ULL)|(u[4]<<24ULL)|(u[3]<<32ULL)|(u[2]<<40ULL)|(u[1]<<48ULL)|(u[0]<<56ULL);
return true;
}
Shouldn't uint8_t << uint64_t have an implicit cast like this: (uint64_t) uint8_t << uint64_t?
TL;DR - No, shift operators are special.
Full answer
The behaviour you're describing (essentially, matching operand types) is known as the usual arithmetic conversions in the C standard.1
We see that the standard mandates this for many operators (e.g. for additive operators):
[6.5.6] If both operands have arithmetic type, the usual arithmetic conversions are performed on them.
However, we see no such phrase in the equivalent section for bitwise shift operators. The closest we have is:
[6.5.7] The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand.
However, the integer promotions are a different thing - they say (essentially) that any type smaller than [unsigned] int is converted to an [unsigned] int.
So the compiler is correct to warn here. (And as I'm sure you can guess, the solution is to perform an explicit cast on the left-hand operand ;)
1. For the purposes of this answer, I'm considering C11 (specifically N1570) canonical. The behaviour is the same at least as far back as C99.
This answer suggests that I can promote either of the operands and the whole expression will cast to unsigned long long, but it might be wrong.
It is.
The standard says:
6.5.7 Bitwise shift operators
The integer promotions are performed on each of the operands. The type of the result is that of the promoted
left operand. If the value of the right operand is negative or is
greater than or equal to the width of the promoted left operand, the
behavior is undefined
Which means that you need to cast the left operand, casting the right one doesn't change anything here.
The answer you linked is misleading. It seems to suggest that you can widen either operand and it will trigger automatic conversion of the other operand to the common (wider) type. That is true for most binary operators in C. However, that is not true for shift operators.
The shift operators in C are actually special in that regard. They behave asymmetrically. Changing the type of the right operand to a wider type does not trigger the conversion of the left operand to the type of the right operand, and does not affect the resultant type. The resultant type is always defined by the (possibly promoted) type of the left operand.
In your case you specifically have to convert the left operand to unsigned long long type.
The result of the left and right shift operators has the same type as the left operand. So the type of the right operand won’t affect the type of the result.
You need to cast the left operand to unsigned long long to get the desired result.
Even though I cast every right operand to unsigned long long, the
warning persists. Shouldn't uint8_t << uint64_t have an implicit
cast like this: (uint64_t) uint8_t << uint64_t?
No.
This answer suggests that I can promote either of the operands and the
whole expression will cast to unsigned long long, but it might be
wrong.
I see why you think the linked answer says that, but it demonstrates using a constant of type unsigned long long as the left operand. That part is correct. To the extent that the answer suggests that the same could have been achieved by using a wider type on the right, it is misleading.
The standard specifies:
The integer promotions are performed on each of the operands. The type
of the result is that of the promoted left operand. If the value of
the right operand is negative or is greater than or equal to the width
of the promoted left operand, the behavior is undefined.
(C2011, 6.5.7/3)
Note well that the type of the result expression is determined by the type of the left operand only, and that it is the value of the right operand relative to the (promoted) type of the left operand that can trigger this particular undefinedness provision.
You are left-shifting a uint8_t. The integer promotions will be performed on the left operand, resulting in a value of type int. Supposing that your ints are 32 bits wide, the left shifts by 32 or more bits produce undefined behavior, and in some cases you might get additional undefined behavior arising from the fact that it is signed, not unsigned int.
Correct this by casting the left operand. For example, (uint64_t) u[7] << 56.
gcc 4.8.4 warns about 1u << 63ul (assuming 64 bit long and 32 bit int) and computes 0. Rightfully so (no promotion from 1u to 1ul before shifting)?
ISO/IEC 9899:201x, 6.3.1.8 (Usual arithmetic conversions): "Many operators that expect operands of arithmetic type cause conversions"; 6.5.7 (Bitwise shift operators): "The integer promotions are performed on each of the operands...".
But I am not unable to conclude. Which are those "many operators"? As I understand, "integer promotion" does not pertain to types wider than int (am I correct?), but the standard does not explicitly state that the right operand of a bitwise-shift is not taken into account for the implicit type conversion.
Each operation documents this separately. For example, n1548 §6.5.5 "Multiplicative operators" ¶3
The usual arithmetic conversions are performed on the operands.
This phrase is omitted from §6.5.7 "Bitwise shift operators". Instead it says:
The integer promotions are performed on each of the operands. The type of the result is the type of the promoted left operand. …
Since the section on bitwise shift operators says nothing about "usual arithmetic conversions", that conversion does not happen.
The "usual arithmetic conversions" include conversions to floating point types and how to determine a common type for two operands.
Bitshifts do not operate on floating point types, only integers (constraint, 6.5.7p2). Different than for other binary operators taking integers only (e.g. bit-and), the two operands are not directly combined for the result; there is no requirement to have a common type for the operation. Thus, each operand is independently promoted (from your citation: "The integer promotions are performed on each of the operands").
Reading the whole paragraph 6.5.7p3 makes it clear:
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
Note the emphasised sentence, which clarifies the result type is solely determined from the left operand. From that and the last sentence follows that int or unsigned int for the right operand is more than sufficient for all current implementations. The lowest upper limit of int (INT_MAX) is 32767 - much more than the number of bits in any standard type of any implementation, even if we consider future wider integers with 1024 and more bits.
Also note that your code invokes undefined behaviour, unless your platform has an unsigned int with at least 64 bits (last sentence).
The compiler correctly warns about your code invoking undefined behaviour. The warning is not required, but you should be glad it does. Treat it seriously! Any behaviour of the program is correct if you invoke undefined behaviour.
If you want a 64 bit type, use uint64_t. For a constant, use the macro UINt64_C(1) which generates an integer constant with at least 64 bits (uint_least64_t). Both are provided by stdint.h.
About your other question:
From 6.3.1.1p2:
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.
This seemingly trivial line is taken from the C book my Mike Banahan & Brady (Section 2.8.8.2).
I can understand how implicit promotion comes into play in expressions like c=a+b depending on the types of the operands, but I am unable to grasp how and in which case the same can figure in something like -b, where b is any legitimate operand. Can you explain it and then give a proper example?
Extracted text follows:
The usual arithmetic conversions are applied to both of the operands
of the binary forms of the operators. Only the integral promotions are
performed on the operands of the unary forms of the operators.
Update:
Lest it goes unnoticed, here I am adding what I asked based on OUAH's answer in a comment–
The book says 'Only the integral promotions are performed'...Does it mean that in an expression like x=-y, where 'x' is a long double and 'y' is a float, 'y' won't be promoted to long double if we explicitly use a unary operator? I know it would be, but asking it nevertheless to get a clearer idea about the "Only the integeral promotions..." part.
Update:
Can you explain with example how promotion comes into play for the following bit-wise operators? For the last three, should I assume that whenever those are used on a variable, it is promoted to integer type first? And what exactly does the "usual arithmetic conversions" mean for the first three? Can you give a small example? I don't want to post it as a separate question if it can be settled here.
Take this example on a 32-bit system:
unsigned char a = 42;
printf("%zu\n", sizeof a); // prints 1
printf("%zu\n", sizeof +a); // prints 4, a has been promoted to int
For a unary arithmetic operator, the C standard says (in section 6.5.3.3) that
The integer promotions are performed on the operand, and the result has the promoted type.
It also defines the term, in section 6.3.1.1:
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.
(References are to the N1570 draft of the 2011 C standard.)
I believe the rationale for this is that implementations are not required to support any arithmetic operations on types narrower than int (one "word"). Operands narrower than an int are converted to int, or to unsigned int, before they're operated on.
For binary operators (those taking two operands), there's an additional requirement, that both operands must be of the same type. Typical CPUs might have instructions to add two 32-bit signed integers, or two 32-bit unsigned integers, or two 64-bit signed or unsigned integers, but none that will directly add, for example, a 32-bit signed integer and a 64-bit unsigned integer. To allow for this, we have the usual arithmetic conversions, described in section 6.3.1.8. These rules tell you, for example, what happens when you try to add an int to a double: the int operand is promoted, by conversion, to type double, and the addition adds the resulting two double operands.
The shift operators don't require the usual arithmetic conversions, since there's no particular need for both operands to be of the same type. The left operand is a value to be operated on; the right operand specifies the number of bits to shift it.
Does it mean that in an expression like x=-y, where x is a long double and y is a float, y won't be promoted to long double if we explicitly use a unary operator?
Assignment causes the right operand to be converted to the type of the left operand. The expression -y is evaluated independently of the context in which it appears (this is true for most expressions). So the unary - is applied to its operand, which is of type float (the integer promotions don't affect that), yielding a result of type float. The assignment causes that float value to be converted to long double before being assigned to x.
The title of your question asks how this can possibly happen. I'm not sure what that means. The conversion rules are specified in the language standard. Compilers follow those rules.
I'm not sure, but I think that every operation is promoted to a proper type. First the conversion is done, and secondly the operation is done. The -b operation changes the value of the result variable, so the promotion should be done and then the value sign is converted.
Operations like +b is also an operations, so there are an Promotion + Operation process. I don't know if a code optimization coud skip this process in this concrete case.
During operations with binary operators arithmetic promotion to highest needed form is done, from int to long, float, or double.
double c=2+3.5
But in unary operators +,- only promotions in Integer type datatypes are allowed. From short to int or long.
unsigned char a=255;
cout<<sizeof(a)<<endl; //prints 1
cout<<sizeof(+a)<<endl; //prints 4
cout<<sizeof(++a)<<endl; //prints 1
So this Integral promtions does not work on other unary operators ++a and a++