Reputation: 750
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;
}
Upvotes: 0
Views: 1856
Reputation: 272557
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.
Upvotes: 5
Reputation: 180418
Even though I cast every right operand to
unsigned long long
, the warning persists. Shouldn'tuint8_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.
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 int
s 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
.
Upvotes: 1
Reputation: 320551
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.
Upvotes: 2
Reputation: 224112
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.
Upvotes: 1
Reputation: 1247
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.
Upvotes: 4