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
For the following program:
int main(void)
{
int value = 2;
int result = value >> 1U;
return result;
}
...Splint 3.1.2 gives the warning:
splint_test.c: (in function main)
splint_test.c:4:18: Variable result initialized to type unsigned int, expects
int: value >> 1U
To ignore signs in type comparisons use +ignoresigns
Splint seems to be claiming that an expression where a signed integer is shifted right has the type of an unsigned integer. However, all I can find in the ANSI C90 standard is:
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity, 2 raised to the power E2.
The primary target for this code is an embedded system with a mostly-C90 compiler. However, I'm interested in writing standards-compliant code. I have been testing on GCC and Clang in C99 mode so that restrict works.
My questions are:
Does the C standard make any claims about the type of the result of a bit-shift?
Do compilers?
If not, why might Splint be issuing this warning?
It's a bug in Splint. Splint wrongly assumes that the type of e1 << e2 is ctype_wider(te1, te2). The correct type would be just te1.
The buggy code starts here by using the same code path for the bitwise operators like &, | and ^, as well as for the << and >> operators.
The actual bug is at the end of that code, which assumes that for all these bitwise binary operators, the return type is ctype_wider(te1, te2).
I have opened a bug on Splint's GitHub issue tracker, referencing this question.
Update, February 2021:
NetBSD's lint says that in traditional C, the bitwise shift operators applied the usual arithmetic conversions to their operands:
/* Make sure both operands are of the same type */
if (mp->m_balance_operands || (tflag && (op == SHL || op == SHR)))
balance(op, &ln, &rn);
Explanation of the code:
tflag is the flag for checking traditional C programs, as opposed to C90 or C99
mp->m_balance_operands is false for the bitwise shift operators, defined in this table, the b column in the middle
balance performs the usual arithmetic conversions
This was a change in C90. The Splint code may thus have been correct for traditional C, maybe it just had not been updated for C90 or C99.
No. The standard says the type of a bitshift is the type of the left operand, promoted:
6.5.7p3
... The type of the result is that of the promoted left operand. ...
Your tool must be confused, inferring the type with usual arithmetic conversion, which applies to most binary operators but not << and >>.
You can also verify the type is int by inserting a _Generic-based type assert and observing that compilers accept it:
int main(void)
{
int value = 2;
int result = _Generic(value >> 1U, int: value>>1U); //compiles, the type is int
return result;
}
The C99 through C17 standards say:
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand.
Since value is an int it requires no promotion and the type of the "promoted left operand" is int, and the type of the result of << is the same.
C89/C90 says the same except with the word "integer" replaced by "integral".
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++
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).