34

In all versions of C and C++ prior to 2014, writing

1 << (CHAR_BIT * sizeof(int) - 1)

caused undefined behaviour, because left-shifting is defined as being equivalent to successive multiplication by 2, and this shift causes signed integer overflow:

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] 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.

However in C++14 the text has changed for << but not for multiplication:

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. [...] Otherwise, if E1 has a signed type and non-negative value, and E1 × 2E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.

The behaviour is now the same as for out-of-range assignment to signed type, i.e. as covered by [conv.integral]/3:

If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

This means it's still non-portable to write 1 << 31 (on a system with 32-bit int). So why was this change made in C++14?

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 2
    +1 Howard Hinnant [comments on this topic here](http://stackoverflow.com/questions/19593938/is-left-shifting-a-negative-integer-undefined-behavior-in-c11#comment29091986_19593938), the only reason I remember that comment is that comment was part of my inspiration for this [question](http://stackoverflow.com/q/21319413/1708801). – Shafik Yaghmour Oct 13 '14 at 02:20
  • Imagine if they had instead simply stated something like: "Constraints: After promotion, the left operand E1 shall be an unsigned integer type". It would have saved mankind from astronomic amounts of subtle, shift-related bugs. – Lundin Jan 18 '17 at 13:50

1 Answers1

20

The relevant issue is CWG 1457, where the justification is that the change allows 1 << 31 to be used in constant expressions:

The current wording of 5.8 [expr.shift] paragraph 2 makes it undefined behavior to create the most-negative integer of a given type by left-shifting a (signed) 1 into the sign bit, even though this is not uncommonly done and works correctly on the majority of (twos-complement) architectures:

...if E1 has a signed type and non-negative value, and E1 * 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

As a result, this technique cannot be used in a constant expression, which will break a significant amount of code.

Constant expressions can't contain undefined behavior, which means that using an expression containing UB in a context requiring a constant expression makes the program ill-formed. libstdc++'s numeric_limits::min, for example, once failed to compile in clang due to this.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Writer of that wording seems confused... the new rule doesn't allow one to "create the most-negative integer of a given type by left-shifting a (signed) 1 into the sign bit" either. The result is implementation-defined, which is a far cry from being guaranteed to give the most-negative value. – Ben Voigt Oct 12 '14 at 23:51
  • @BenVoigt For two's complement there is only one sane result. And that is consistently implemented in all relevant and common compilers. – Columbo Oct 12 '14 at 23:52
  • 3
    @Loopunroller: The last Standard quote in the question is worded that way to avoid requiring that conversion of an unsigned value to signed follow two's-complement rules. Also, it's not true that for two's complement there is only one sane result. Saturating arithmetic is also a useful, sane choice. One could even say that's more sane than doubling a positive value creating a negative. – Ben Voigt Oct 12 '14 at 23:54
  • 1
    Was it actually so painful to have to write `- max() - 1` that they decided to change the standard instead? – M.M Oct 13 '14 at 00:09
  • @MattMcNabb Perhaps Howard Hinnant will see this and comment on whether he has other cases of code breakages in mind. – T.C. Oct 13 '14 at 00:54
  • 13
    +1 Fwiw, libc++ computes `max()` in terms of `min()`, and so writing `min()` in terms of `max()` would have been excessively entertaining. :-) Bottom-line as far as I'm concerned: I'm tired of standing on my head to support architectures which are not targeted by **any** modern C++ compiler. Two's complement signed integers are our reality, now, and in the future. – Howard Hinnant Oct 13 '14 at 17:11
  • @Columbo: That there may only be one sane behavior for something does not prevent compilers from implementing crazy behaviors. – supercat Apr 17 '15 at 23:48