Reputation: 444
I want to make sure I understand exactly under what circumstances the <<
and >>
operators in C produce Undefined Behavior. This is my current understanding:
Let...:
x_t
be the type of x
after integer promotionN
be the bitwidth of x
after integer promotionM
be the number of 0s to the left of the most-significant 1 bit in the representation of x
after integer promotionx << y
is UB if any of the following:
x < 0
(even if y == 0
)y < 0
y >= N
x_t
is a signed type and y >= M
x >> y
is UB if any of the following:
y < 0
y >= N
...and is implementation defined if:
x < 0
If I have this understanding correct, it would imply the following:
unsigned short x = 1;
x << 31;
This would be undefined behavior in the case where int
is 32 bits and short
is 16 (because x
would be promoted to int
, and the left shift by 31 would put the 1 bit into position 31), but it would be defined behavior in the case where int
and short
are both 32 bits (because x
would be promoted to an unsigned int
and 31 < 32).
Upvotes: 1
Views: 187
Reputation: 81115
Under C89, the behavior of left-shifting an N-bit integer left by 0..N-1 bits was unambiguously defined for all possible signed or unsigned values of the integer, except on platforms where signed and unsigned types had padding bits in different places. On non-two's-complement platforms, however, behaving in the mandated function may have been less useful than e.g. processing <<
as a "multiply by power of two" operator, and more expensive than allowing compilers select in arbitrary fashion from among platform-specific interpretations (e.g. sometimes processing x<<1
as (x+x) and sometimes processing it by actually shifting x
left by one bit).
Because there was no reason to imagine that implementations for two's-complement platforms would deviate from the C89 behavior, even if allowed to do so, and because people who worked with other platforms would be better placed than the Committee to weigh the pros and cons of handling the construct in precisely-predictable predictable or somewhat-unpredictable fashion, the C99 Committee opted to waive jurisdiction over the behavior of left-shifting negative numbers. The Committee classified left-shifts of negative numbers as Undefined Behavior because they never imagined that its characterization of actions as "non-portable or erroneous" would be twisted to imply that the Committee judged such actions "non-portable, and therefore erroneous".
Upvotes: -1
Reputation: 213276
What you have written here is far more cumbersome to comprehend than the actual standard... the TL;DR of C17 6.5.7 can be summarized as:
Done, that's it. No need to make things more complicated.
The golden rule is: never perform bitwise arithmetic on signed types ever. Abide it and you'll avoid a number of well-known bugs.
Upvotes: 1
Reputation: 385506
Yes.
I find your definition of M
a little weak. Specifically, it wasn't clear to me if you were including the sign bit.
But yes, the interpretation is correct.
- 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.
y < 0
⇒ UBy >= N
⇒ UBThe result of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are filled with zeros. IfE1
has an unsigned type, the value of the result isE1
× 2E2
, reduced modulo one more than the maximum value representable in the result type. IfE1
has a signed type and nonnegative value, andE1
× 2E2
is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
This paragraph is poorly worded.
There's no doubt that the behaviour of E1 << E2
isn't defined when E1
× 2E2
isn't representable.
x << y
, x_t
is a signed type and y >= M
⇒ UB[1]But what about when "E1
has a signed type and nonnegative value" is false? 3 << 2
is clearly not undefined behaviour, so that means neither the "then" not the "otherwise" clauses apply when this is false, so that means the spec is silent on the behaviour of -3 << 2
. It's literally behaviour that's not defined by the spec. So,
x << y
, x < 0
⇒ UBThe result of
E1 >> E2
isE1
right-shiftedE2
bit positions. IfE1
has an unsigned type or ifE1
has a signed type and a nonnegative value, the value of the result is the integral part of the quotient ofE1
/2E2
. IfE1
has a signed type and a negative value, the resulting value is implementation-defined.
x >> y
, x < 0
⇒ Implementation definedUpvotes: 3
Reputation: 140880
Do I have correct understanding of Undefined Behavior for shift operators in C?
Yes, whereas the M
part is a little vague.
While the any of the following:
lists some examples, it does not exhaust the whole possible space of behaviors. INT_MAX << 1
is also UB. The rule is x * 2**y <= X_T_MAX
, where X_T_MAX
is the maximum representable value in type x_t
. That is a 2D plane of allowed numbers.
Upvotes: 1