Definition
The problem and the solutions:
- The problem. When an int operation exceeds
Integer.MAX_VALUE(2,147,483,647), the result wraps toInteger.MIN_VALUE(-2,147,483,648) with no warning. - Detection.
Math.addExact(),Math.subtractExact(),Math.multiplyExact()throwArithmeticExceptionon overflow. - Prevention. Use
long(range up to 9.2 quintillion),BigInteger(unlimited), orBigDecimal(exact decimals).
An odometer that goes from 999,999 back to 000,000 without a warning light. If a bank counter wraps like this, sequence numbers repeat, balances go negative, and nobody knows until the audit. Math.addExact() is the warning light that stops the engine instead of letting it wrap.
Understanding the Concepts
What happens on integer overflow?
int max = Integer.MAX_VALUE; // 2,147,483,647
int overflowed = max + 1; // -2,147,483,648 (wraps to MIN_VALUE!)
System.out.println(max); // 2147483647
System.out.println(overflowed); // -2147483648 (no exception, no warning)
System.out.println(max + max); // -2 (silently wrong)
Java does not throw an exception. The result is silently wrong. This is one of the most dangerous behaviors in banking code.
Why is this dangerous in banking?
// Scenario: transaction counter wraps from MAX_VALUE to MIN_VALUE
int txnCounter = Integer.MAX_VALUE;
txnCounter++; // -2147483648 → next sequence ID is negative!
// Scenario: adding two large balances overflows
int balanceA = 1_500_000_000; // 1.5 billion
int balanceB = 1_000_000_000; // 1.0 billion
int total = balanceA + balanceB; // -794967296 (WRONG!)
Silent overflow can produce:
- Negative balances that pass validation ("balance > 0" is false)
- Duplicate sequence IDs when a counter wraps
- Incorrect totals in batch reports
How do you detect overflow with Math.addExact?
try {
int safe = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage()); // integer overflow
}
try {
long safe = Math.multiplyExact(Long.MAX_VALUE, 2);
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage()); // long overflow
}
// Available methods:
// Math.addExact(int, int) / Math.addExact(long, long)
// Math.subtractExact(int, int) / Math.subtractExact(long, long)
// Math.multiplyExact(int, int) / Math.multiplyExact(long, long)
// Math.incrementExact(int) / Math.incrementExact(long)
// Math.decrementExact(int) / Math.decrementExact(long)
// Math.negateExact(int) / Math.negateExact(long)
// Math.toIntExact(long) – throws if long does not fit in int
How do you prevent overflow by using larger types?
// Use long instead of int for large values
long balanceA = 1_500_000_000L;
long balanceB = 1_000_000_000L;
long total = balanceA + balanceB; // 2,500,000,000 (correct)
// Use BigInteger for arbitrarily large values
import java.math.BigInteger;
BigInteger huge = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
System.out.println(huge); // 9223372036854775808 (no overflow, ever)
// Use BigDecimal for money
import java.math.BigDecimal;
BigDecimal total2 = new BigDecimal("1500000000")
.add(new BigDecimal("1000000000"));
System.out.println(total2); // 2500000000 (exact)
How do you check before the operation?
// Manual check: will a + b overflow?
public static int safeAdd(int a, int b) {
if (b > 0 && a > Integer.MAX_VALUE - b) {
throw new ArithmeticException("Overflow: " + a + " + " + b);
}
if (b < 0 && a < Integer.MIN_VALUE - b) {
throw new ArithmeticException("Underflow: " + a + " + " + b);
}
return a + b;
}
// But Math.addExact does this for you – just use it
What about long overflow?
long can also overflow, just at a much larger range.
long maxLong = Long.MAX_VALUE; // 9,223,372,036,854,775,807
long overflowed = maxLong + 1; // -9,223,372,036,854,775,808
try {
long safe = Math.addExact(Long.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Long overflow caught");
}
If your values can exceed even long range (very rare in banking), use BigInteger.
What about multiplication overflow?
Multiplication is especially dangerous because small-looking numbers can overflow:
int a = 100_000;
int b = 100_000;
int product = a * b; // 1410065408 (WRONG! should be 10,000,000,000)
long correct = (long) a * b; // 10000000000 (correct: one operand cast to long)
try {
int safe = Math.multiplyExact(a, b); // throws ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Multiplication overflow");
}
What are common mistakes?
- Assuming int is big enough for money. Indian bank accounts can hold crores. 100 crore = 1 billion, which fits in int, but 300 crore = 3 billion, which overflows. Always use long or BigDecimal for money.
- Counter wrapping. An int counter that increments once per transaction wraps after ~2 billion transactions. Use long.
- Ignoring Math.addExact in non-critical code. Overflow bugs are silent and can hide for months until the right values trigger them.
- Casting long to int without checking.
(int) longValuesilently truncates. UseMath.toIntExact(longValue)which throws on overflow.
What should a senior engineer know?
- Compilers and static analyzers do not catch overflow.
javachappily compilesInteger.MAX_VALUE + 1. It is not a compile error. - FindBugs/SpotBugs has rules like
INT_OVERFLOWthat detect potential overflow. Enable in CI. - In Kotlin,
Int.plus()also wraps. But Kotlin'sLongtype is the same 64-bit as Java's. - In databases, use BIGINT for counters. PostgreSQL INT maxes at ~2 billion. BIGINT handles ~9.2 quintillion.
- Java 8+ added all the Exact methods. Before Java 8, you had to write manual overflow checks.
Visual Overview
Integer overflow:
MAX_VALUE + 1 → MIN_VALUE (wraps, no exception)
100000 * 100000 → 1410065408 (wrong, no exception)
Detection:
Math.addExact(MAX, 1) → ArithmeticException
Math.multiplyExact(a, b) → ArithmeticException
Math.toIntExact(longValue) → ArithmeticException if too large
Prevention:
Use long: max ~9.2 quintillion (enough for most banking)
Use BigInteger: unlimited (rare need)
Use BigDecimal: exact decimals (money)
Decision tree:
Value < 2 billion? → int is safe
Value < 9.2E18? → long is safe
Value can be larger? → BigInteger
Money? → BigDecimal (always)
Key Takeaway
Java integer arithmetic wraps silently on overflow. Integer.MAX_VALUE plus one does not throw an exception – it quietly becomes Integer.MIN_VALUE. This is one of the most dangerous behaviors in banking code because it can produce wrong balances, duplicate sequence IDs, and negative counters with no visible error.
The detection tool is Math.addExact(), Math.multiplyExact(), and their siblings, all added in Java 8. They throw ArithmeticException when the result would overflow, giving you a loud failure instead of a silent wrong answer. Use them in any calculation where overflow could produce a wrong monetary value.
The prevention strategy is to use the right type. Use long (up to 9.2 quintillion) for counters and large values. Use BigDecimal for all money arithmetic. Use BigInteger for the rare cases where even long is not enough. And when casting a long to an int, use Math.toIntExact() instead of a raw cast to catch overflow.
One sentence to remember: Java integer overflow wraps silently with no exception – use Math.addExact() to detect it, long for large values, and BigDecimal for money.
---