Why Signed 32-Bit Integer Left Shift by 31 Causes UB
In systems programming languages like C and C++, shifting a signed
32-bit integer left by 31 positions (1 << 31)
triggers undefined behavior because the operation attempts to produce a
value that exceeds the maximum representable positive value of the
signed type. A 32-bit signed integer reserves its most significant bit
as the sign bit, leaving only 31 bits for the magnitude of non-negative
values. Shifting a positive value into the sign bit causes an arithmetic
overflow according to the language specifications, leading standard
compilers to treat the operation as undefined.
The Bit Layout of a Signed 32-Bit Integer
A standard signed 32-bit integer (such as int32_t or a
typical 32-bit int) consists of 32 binary digits: *
Bits 0 to 30: 31 magnitude (value) bits. * Bit
31 (MSB): 1 sign bit, where 0 indicates a positive
number and 1 indicates a negative number in two’s
complement representation.
Because bit 31 is designated for the sign, the maximum positive value that can be represented is \(2^{31} - 1\), which equals \(2,147,483,647\). The minimum negative value representable in two’s complement is \(-2^{31}\), which equals \(-2,147,483,648\).
Mathematical Definition of the Left Shift
In the C and C++ language standards, the left-shift operator
E1 << E2 on a signed integer is defined
mathematically as:
\[\text{Result} = E1 \times 2^{E2}\]
When evaluating 1 << 31: 1. The operand
1 is multiplied by \(2^{31}\). 2. The mathematical result is
\(2,147,483,648\). 3. This value (\(2,147,483,648\)) is strictly greater than
the maximum representable signed positive integer (\(2,147,483,647\)).
Because the true mathematical product cannot fit inside the positive range of a signed 32-bit integer, an arithmetic overflow occurs.
Language Specifications and Undefined Behavior
According to ISO C standards (such as C99 and C11 §6.5.7), if the
operand E1 has a signed type and non-negative value, and
\(E1 \times 2^{E2}\) is not
representable in the result type, the behavior is explicitly
undefined.
This rule was established for several reasons: * Hardware Portability: Early computing architectures did not universally use two’s complement arithmetic. Ones’ complement and sign-magnitude systems handle bit 31 differently, making bit-shifting directly into the sign bit non-portable. * Compiler Optimizations: Defining signed overflow as undefined behavior allows optimizing compilers to assume signed integers will never overflow. This enables aggressive loop vectorization, constant folding, and dead code elimination.
Modern Standards and Safe Alternatives
While C++20 formally adopted two’s complement arithmetic and defined
1 << 31 as yielding the signed value \(-2,147,483,648\), the operation remains
undefined behavior in standard C (up through C17) and pre-C++20
standards.
To safely shift 31 bits without invoking undefined behavior, the shift must be performed using an unsigned 32-bit integer, where bit 31 is part of the standard numeric range:
// Undefined Behavior in C99/C11/C17:
int32_t a = 1 << 31;
// Defined and Safe:
uint32_t b = 1U << 31;
int32_t c = (int32_t)(1U << 31); // Explicit cast to signedUsing 1U << 31 ensures that the calculation
operates within the unsigned range of \(0\) to \(4,294,967,295\), preventing signed
arithmetic overflow.