AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

02 Types & operators / Lesson 4 of 7

Integer arithmetic, overflow and the Math class

14 min · +10 XP · 2 exercises · 5 questions

Read the text14 min
Exercises0/2
Quiz5 q

Quick answer

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.

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:

MethodNote
abs, max, minOverloaded 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, hypotsqrt(-1) is NaN
floor, ceil, round, rintround(double) → long, round(float) → int; rounds half up (round(-2.5) is -2)
floorDiv, floorModThe mathematical versions of / and %
addExact … toIntExactOverflow-checked
random()A double in [0, 1); for anything serious use java.util.Random or ThreadLocalRandom
PI, EConstants
sin, cos, log, log10, expStandard 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 in long.
  • "-7 % 3?" -1. "How do you get 2?" Math.floorMod(-7, 3).
  • "Why low + (high - low) / 2?" Avoids overflow in low + high.
  • "Is i = i++ a bug?" Yes: i is unchanged — the post-increment value is overwritten by the assignment.

Key takeaways

  • Overflow wraps silently; Math.abs(MIN_VALUE) is negative; multiply in long by casting an operand.
  • Math.addExact and friends throw on overflow.
  • / truncates toward zero and % takes the dividend's sign; use floorMod for a true modulo.
  • Integer division by zero throws; floating-point division by zero gives Infinity/NaN.
  • BigInteger when long is 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.

Mark as readSign in to keep your place.

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:

MethodNote
abs, max, minOverloaded 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, hypotsqrt(-1) is NaN
floor, ceil, round, rintround(double) → long, round(float) → int; rounds half up (round(-2.5) is -2)
floorDiv, floorModThe mathematical versions of / and %
addExact … toIntExactOverflow-checked
random()A double in [0, 1); for anything serious use java.util.Random or ThreadLocalRandom
PI, EConstants
sin, cos, log, log10, expStandard 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 in long.
  • "-7 % 3?" -1. "How do you get 2?" Math.floorMod(-7, 3).
  • "Why low + (high - low) / 2?" Avoids overflow in low + high.
  • "Is i = i++ a bug?" Yes: i is unchanged — the post-increment value is overwritten by the assignment.

Key takeaways

  • Overflow wraps silently; Math.abs(MIN_VALUE) is negative; multiply in long by casting an operand.
  • Math.addExact and friends throw on overflow.
  • / truncates toward zero and % takes the dividend's sign; use floorMod for a true modulo.
  • Integer division by zero throws; floating-point division by zero gives Infinity/NaN.
  • BigInteger when long is 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 fits

Digit 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=1

In this module: Types & operators

← Conversions, promotion and casting · Floating point — doubles, precision and BigDecimal →