What is 2147483647 + 1? Integer overflow in Java, Go, Rust, C and Python

In Java int, Go int32 and Rust i32, 2147483647 + 1 overflows: Java and Go wrap to a negative, Rust panics or wraps by Cargo profile, C's INT_MAX + 1 is undefined, Python gives 2147483648.

DevelopmentNumbers

The largest value a 32-bit signed integer can hold is 2,147,483,647. Java’s int, Go’s int32 and Rust’s i32 all use that range. When you add 1 to it, the language and type rules decide which result a program may rely on. Where a language leaves the width of a type open, the implementation and target platform matter too. The same addition gives a negative number in Java, stops the program in Rust depending on how you built it, and has no defined result at all in C.

Language and type When a signed integer goes out of range
Java int (always 32-bit) Only the low-order bits are kept (-2,147,483,648). No exception
Go int32 Only the low-order bits are kept (-2,147,483,648). No panic
Rust i32 Panic if overflow checks are on; two’s-complement wraparound (-2,147,483,648) if they are off
C int (INT_MAX + 1) Undefined behavior. The width of int varies by implementation
Python int Arbitrary precision. Ordinary integer arithmetic has no fixed-width overflow

A number line for the 32-bit signed integer range. The left end is the minimum, -2,147,483,648 (hex 80000000), the middle is 0, and the right end is the maximum, 2,147,483,647 (hex 7fffffff). An arrow labelled +1 runs from the right end back to the left end. Below, a table shows the result of 2147483647 + 1: Java int and Go int32 wrap to -2,147,483,648; Rust i32 with the default Cargo profiles panics in a dev build and gives -2,147,483,648 in a release build; in C with a 32-bit int, INT_MAX + 1 is undefined behavior; Python int simply represents 2,147,483,648.

Java and Go: a quiet wraparound

Java’s int is always 32 bits (JLS 4.2.1). The Java Language Specification (JLS 15.18.2) says that when integer addition overflows, the result is the low-order bits of the mathematical sum written in a sufficiently large two’s-complement format. The result’s sign can therefore differ from the sign of the true sum. JLS 4.2.2 adds that integer operators do not indicate overflow or underflow in any way.

That is why Integer.MAX_VALUE + 1 becomes Integer.MIN_VALUE, -2,147,483,648, without an exception.

Use Math.addExact when overflow should be an error. It throws ArithmeticException when the result does not fit in an int or long.

int a = Integer.MAX_VALUE;
int wrapped = a + 1;                // -2147483648
int checked = Math.addExact(a, 1);  // ArithmeticException

The “Integer overflow” section of the Go specification is similar. Signed +, -, *, / and << may legally overflow. The integer representation, operation and operands determine the result, and overflow does not cause a run-time panic. Go represents n-bit integers in two’s complement, so adding 1 to the maximum int32 gives -2,147,483,648.

The specification also says that a compiler may not optimize on the assumption that overflow does not occur. It may not, for instance, assume that x < x + 1 is always true.

var a int32 = math.MaxInt32
b := a + 1 // -2147483648

The type matters here. Go’s plain int is either 32 or 64 bits wide depending on the implementation. With a 64-bit int, 2147483647 + 1 does not overflow and gives 2,147,483,648.

This rule covers values computed at run time. Go evaluates constant expressions exactly, and a typed constant must be exactly representable in its type. An out-of-range typed constant is a compile error.

Rust: it depends on the build

The Rust Reference treats arithmetic overflow as a programmer error. In builds with debug_assert! enabled, the implementation must insert checks that panic on overflow. In other builds, the implementation may either panic or silently wrap. If it wraps, it must follow two’s-complement rules.

Separately from the language rule, Cargo profiles set the defaults commonly used in practice. Cargo’s default dev profile has overflow-checks = true, while its default release profile has overflow-checks = false. A project can override either setting. With the default profiles, i32::MAX + 1 panics during development and returns -2,147,483,648 in a release build.

You can record the intended behavior in the method name.

let a = i32::MAX;
a.checked_add(1);     // None
a.wrapping_add(1);    // i32::MIN
a.saturating_add(1);  // i32::MAX

C: no promises for signed integers

The C standard does not say that int is 32 bits. In C, it is more accurate to discuss the expression INT_MAX + 1. The number 2,147,483,647 is INT_MAX only on implementations where that is the maximum value of int.

Under §6.5.1 of N3220, the C23 working draft, if an expression produces a result outside the range representable by its type, the behavior is undefined. Undefined behavior is behavior for which the standard imposes no requirements. Nothing guarantees that INT_MAX + 1 becomes INT_MIN, and nothing guarantees that the program stops.

Unsigned integers are different. Under §6.2.5, unsigned arithmetic is performed modulo 2^N and cannot overflow, so UINT_MAX + 1u is 0.

To detect overflow in C, compare against the range before adding. When b > 0, a + b overflows if a > INT_MAX - b. GCC also provides __builtin_add_overflow.

int r;
if (__builtin_add_overflow(INT_MAX, 1, &r)) {
    /* handle overflow */
}

According to the GCC manual, this function adds the operands in infinite precision, stores the converted result through its third argument, and returns true when the stored value differs from the infinite-precision result.

Python: arbitrary-precision integers

According to the Python documentation, int values have unlimited precision, so ordinary integer addition does not overflow at a fixed-width boundary. 2147483647 + 1 evaluates to 2147483648.

Conversions or interfaces that require a fixed-width value still need a range check.

Where the 32-bit boundary shows up

The number 2,147,483,647 is 7fffffff in hexadecimal, while 2,147,483,648 is 80000000. Adding one to 7fffffff to get 80000000 sets the top bit and clears every lower bit. In 32-bit two’s complement, that exact bit pattern is the minimum value, -2,147,483,648. You can check both values in the base converter.

For a related discussion of signed 32-bit Unix time, see Unix time and the 2038 problem. For another case where languages disagree, this time on division, see why -7 % 3 differs between languages.

If you are porting sums or counters from another language, first check the type and its width. Then decide whether overflow is an error or a wrapped value is acceptable, and use a function whose name says so, such as Math.addExact, checked_add or wrapping_add.

Specifications and documentation

Back to the blog