Java Integer Overflow: Division, Modulo and the Math Class
Java int arithmetic wraps silently on overflow. How to detect it with Math.addExact, why % keeps the dividend's sign, floorMod, safe midpoints and BigInteger.
- Course: Java study plan
- Module: Types & operators
- Kind: Lesson
- Reading time: 14 min
- Runtime: OpenJDK 18
What happens when an int overflows in Java?
When an int overflows in Java it wraps around silently: Integer.MAX_VALUE + 1 is Integer.MIN_VALUE, with no exception or warning, because int is a 32-bit two's complement number. To avoid it, compute in long by casting an operand before the operation, use Math.addExact or Math.multiplyExact, which throw ArithmeticException, or use BigInteger.
Lesson
Integer arithmetic in Java is exact, fast, and silently wrong the moment a result leaves the range of its type. Every programmer who has computed the average of two large numbers, or the sum of a big array, or a hash, has met this. The lesson covers what the JVM actually does on overflow, how to detect and avoid it, the rules of / and % with negative numbers, and the tools in java.lang.Math.
Two's complement and wrap-around
An int is 32 bits interpreted in two's complement: the top bit is the sign. Integer.MAX_VALUE is 0x7FFFFFFF; add one and the bits become 0x80000000, which is Integer.MIN_VALUE. Nothing stops this — no exception, no flag, no warning:
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648
System.out.println(Integer.MIN_VALUE - 1); // 2147483647
System.out.println(Math.abs(Integer.MIN_VALUE)); // -2147483648 (!)
System.out.println(-Integer.MIN_VALUE); // -2147483648 (!)
System.out.println(1_000_000 * 1_000_000); // -727379968
Math.abs(Integer.MIN_VALUE) is negative because 2 147 483 648 does not exist as an int. This exact fact has caused real outages (hash-to-bucket code doing Math.abs(hash) % n and getting a negative index).
Multiplication overflows the earliest: two 5-digit numbers can exceed int. The fix is long — and the cast must happen before the multiplication:
int a = 100_000, b = 100_000;
long wrong = a * b; // -1486618624: overflowed in int, then widened
long right = (long) a * b; // 10000000000
long alsoRight = a * (long) b;
long overflows too, at 9.2 × 10¹⁸ — enough for most counting, not for a factorial of 21 or 2⁶⁴. Beyond that, java.math.BigInteger.
Detecting overflow: Math.*Exact
Since Java 8, the Math class has versions that throw instead of wrapping:
int sum = Math.addExact(a, b); // ArithmeticException on overflow
int prod = Math.multiplyExact(a, b);
long big = Math.multiplyExact((long) a, b);
int neg = Math.negateExact(x);
int narrowed = Math.toIntExact(someLong); // throws if it does not fit
Use these in financial code and anywhere silent wrap-around is worse than a crash. They JIT-compile to a single overflow-checked instruction, so the cost is negligible.
The average-of-two-ints bug
int mid = (low + high) / 2; // overflows when low + high > MAX_VALUE
int mid = low + (high - low) / 2; // safe when both are non-negative
int mid = (low + high) >>> 1; // safe: unsigned shift treats the wrapped sum as a large positive
Binary search in the JDK itself had this bug for nine years. In an interview, writing low + (high - low) / 2 without being asked is a signal.
Division and remainder
Integer / truncates toward zero; % returns a result with the sign of the dividend (the left operand), such that (a / b) * b + (a % b) == a always holds:
7 / 2 → 3 7 % 2 → 1
-7 / 2 → -3 -7 % 2 → -1
7 / -2 → -3 7 % -2 → 1
-7 / -2 → 3 -7 % -2 → -1
So % is a remainder, not a mathematical modulo. A negative index into a circular buffer, (i - 1) % n, is negative when i is 0. The safe forms:
int mod = Math.floorMod(i - 1, n); // always in [0, n) for positive n
int div = Math.floorDiv(-7, 2); // -4: rounds toward negative infinity
int adhoc = ((i - 1) % n + n) % n; // the pre-Java-8 idiom
Division by zero: for integers, x / 0 and x % 0 throw ArithmeticException: / by zero. For floating point they do not — 1.0 / 0 is Infinity and 0.0 / 0 is NaN (next lesson).
Increment, decrement and evaluation order
int i = 5;
int a = i++; // a = 5, i = 6 (post-increment: use, then add)
int b = ++i; // b = 7, i = 7 (pre-increment: add, then use)
int c = i++ + ++i; // 7 + 9 = 16; i = 9. Legal, unreadable. Never write this.
Java evaluates operands left to right, always — unlike C, where such expressions are undefined. It is specified, so you can predict it, but code that relies on it is code reviewers reject.
The Math class
Static methods, no import needed:
| Method | Note |
|---|---|
abs, max, min | Overloaded for int, long, float, double |
pow(double, double) | Returns double — (int) Math.pow(2, 10) is 1024, but beware precision for large results; prefer a loop or 1 << 10 for powers of two |
sqrt, cbrt, hypot | sqrt(-1) is NaN |
floor, ceil, round, rint | round(double) → long, round(float) → int; rounds half up (round(-2.5) is -2) |
floorDiv, floorMod | The mathematical versions of / and % |
addExact … toIntExact | Overflow-checked |
random() | A double in [0, 1); for anything serious use java.util.Random or ThreadLocalRandom |
PI, E | Constants |
sin, cos, log, log10, exp | Standard transcendental functions |
Math.pow in integer code is a smell: it computes in floating point and Math.pow(10, 15) may not be exactly 1e15 when cast back. Use a loop, Math.multiplyExact in a loop, or BigInteger.pow.
Bit tricks worth knowing
x & 1 // 1 if odd
x >> 1 // x / 2 for non-negative x (floors for negative — differs from / )
x << 3 // x * 8 (until it overflows)
x & (x - 1) // clears the lowest set bit; zero iff x is a power of two (x > 0)
Integer.bitCount(x) // popcount
Integer.MAX_VALUE == ~Integer.MIN_VALUE // true
Shifts are covered in the operators lesson; they are here because interviewers ask "divide by two without /".
BigInteger for the rest
import java.math.BigInteger;
BigInteger f = BigInteger.ONE;
for (int i = 2; i <= 50; i++) f = f.multiply(BigInteger.valueOf(i));
System.out.println(f); // 30414093201713378043612608166064768844377641568960512000000000000
Arbitrary precision, immutable (every operation returns a new object), with add, subtract, multiply, divide, mod, pow, compareTo, gcd, isProbablePrime. Slow relative to long — use it when you need it, not by default.
Interview angle
- "Result of
Integer.MAX_VALUE + 1?"Integer.MIN_VALUE. "How do you detect that?"Math.addExact, or check the sign flip, or compute inlong. - "
-7 % 3?"-1. "How do you get 2?"Math.floorMod(-7, 3). - "Why
low + (high - low) / 2?" Avoids overflow inlow + high. - "Is
i = i++a bug?" Yes:iis unchanged — the post-increment value is overwritten by the assignment.
Key takeaways
- Overflow wraps silently;
Math.abs(MIN_VALUE)is negative; multiply inlongby casting an operand. Math.addExactand friends throw on overflow./truncates toward zero and%takes the dividend's sign; usefloorModfor a true modulo.- Integer division by zero throws; floating-point division by zero gives Infinity/NaN.
BigIntegerwhenlongis not enough.
Common questions
Why is Math.abs(Integer.MIN_VALUE) negative?
Its true value, 2,147,483,648, is one more than Integer.MAX_VALUE, so negating Integer.MIN_VALUE wraps back to itself. That is why hashing code that computes Math.abs(hash) % n can still produce a negative index; Math.floorMod(hash, n) cannot.
How does the modulo operator work with negative numbers in Java?
% is a remainder that takes the sign of the dividend, the left operand: -7 % 3 is -1 and 7 % -3 is 1. For a result always between 0 and n minus 1, use Math.floorMod(-7, 3), which gives 2.
Why use low + (high - low) / 2 instead of (low + high) / 2?
low + high can exceed Integer.MAX_VALUE and wrap to a negative number, giving a negative midpoint. low + (high - low) / 2 cannot overflow when both are non-negative, and (low + high) >>> 1 is also safe. The JDK's own binary search had this bug for nine years.
Why does long x = a * b still overflow in Java?
When a and b are int, the multiplication happens in int and overflows before the result is widened to long. Cast an operand first, (long) a * b, so the whole product is computed in 64 bits.
What does i = i++ do in Java?
It leaves i unchanged. The post-increment saves the old value, increments i, and then the assignment writes the saved old value back. Java evaluates operands left to right, so the result is defined, but the statement is always a bug.
Exercises
Products that fit
Read an integer n, then n pairs of integers a b (each within the int range). For each pair print the exact product a × b followed by a space and either fits if the product is within the int range or overflow if it is not.
Compute in long — cast before multiplying.
Input: n, then n lines of two integers. Output: n lines.
Example: 2 then 100000 100000 and -7 3 →
10000000000 overflow
-21 fitsDigit tricks
Read an integer n and n non-negative long values. For each print <x>: digits=<count> sum=<digit sum> reversed=<digits reversed as a number> palindrome=<true|false> gcd=<gcd(x, digit sum)>. Use / 10 and % 10 — no strings — and Euclid's algorithm for the gcd (gcd(x, 0) = x).
Example: 2 then 1221 907 →
1221: digits=4 sum=6 reversed=1221 palindrome=true gcd=3
907: digits=3 sum=16 reversed=709 palindrome=false gcd=1In this module: Types & operators
← Conversions, promotion and casting · Floating point — doubles, precision and BigDecimal →