skip to content

How do Math.addExact and Math.multiplyExact help with integer overflow, and when would you use them?

level: middleimportance: should knowfreq 55%

answer

  1. Same math, throws ArithmeticException on overflow
  2. addExact/subtractExact/multiplyExact/toIntExact
  3. int and long overloads
  4. Use at size/duration/money/trust boundaries
  5. negateExact catches -MIN_VALUE; BigInteger for unbounded

basics

~10 s

They do the same math as + and *, but throw an ArithmeticException if the result overflows instead of silently wrapping. Use them where a wrong number would be dangerous.

solid answer

~40 s

The `Math.*Exact` family (addExact, subtractExact, multiplyExact, incrementExact, decrementExact, negateExact, and toIntExact(long)) performs ordinary integer arithmetic but **throws ArithmeticException on overflow** rather than wrapping silently. There are int and long overloads. You reach for them at trust boundaries or in any computation where a silently wrong (often negative) result would cause a correctness or security problem — sizing an allocation, computing durations, financial counters, ID sequences. The cost is a branch/check per operation, so they are not for tight hot loops where you've already proven the range is safe. They turn a silent latent bug into a loud, immediate failure you can catch and handle. Java's java.time and many JDK internals use them. For genuinely unbounded math, prefer BigInteger instead; *Exact is for staying in int/long while refusing to corrupt data.

code

java · 8 lines
java
long total;
try {
    total = Math.addExact(count, more);   // throws if it overflows long
} catch (ArithmeticException e) {
    throw new IllegalStateException("size overflow", e);
}

int id = Math.toIntExact(longId);         // throws if longId doesn't fit in int

go deeper

for a junior

Knows Math.addExact throws on overflow while + wraps, and can call it.

for a middle

Names the full *Exact family with int/long overloads, knows the exception is unchecked, and picks them for risky computations.

for a senior

Weighs *Exact vs long vs BigInteger vs plain wrap by correctness need and performance cost; applies them at trust boundaries and in JDK-style time/size math.

for a principal

Establishes when checked arithmetic is mandated (untrusted input, sizing, money), balances overhead, and pairs it with static analysis/fuzzing as a defense-in-depth policy.

## The problem they solve Plain Java integer operators (`+`, `-`, `*`, `++`) **wrap silently** on overflow (see the two's-complement wraparound topic). The program continues with a wrong value and no signal. `Math.*Exact` exists to convert that silent failure into an **explicit, immediate exception** so the bug surfaces at the point it happens. ## The methods All live on `java.lang.Math` and have **int and long overloads**: - `addExact(a, b)` — a + b, throws if it overflows the type. - `subtractExact(a, b)` — a - b. - `multiplyExact(a, b)` — a * b. - `incrementExact(a)` / `decrementExact(a)` — a + 1 / a - 1. - `negateExact(a)` — -a (this matters: `-Integer.MIN_VALUE` overflows, because MIN_VALUE has no positive counterpart!). - `toIntExact(long value)` — narrows a long to int, throwing if it doesn't fit. - (Also `Math.absExact`, and `addExact`/`multiplyExact(int, long)` mixed overloads in newer JDKs.) Each computes the true mathematical result and, if it lies outside the type's range, throws **`java.lang.ArithmeticException`** with a message like `"integer overflow"`. `ArithmeticException` is an **unchecked** RuntimeException, so the compiler does not force a try/catch — you opt into handling it. ## A worked example ```java int a = 2_000_000_000; int b = 2_000_000_000; int wrong = a + b; // 4,000,000,000 doesn't fit int -> wraps to -294967296 (silent!) int right = Math.addExact(a, b); // throws ArithmeticException: integer overflow ``` The first line corrupts data silently; the second refuses to. ## When to use them (and when not) **Use them when a wrong result is harmful**, especially at *trust boundaries* and in: - **Allocation / size math** — `count * elementSize` for a buffer; an overflowed small/negative size is a classic security hole. - **Durations / time** — adding nanos/millis; java.time itself uses `*Exact` internally. - **Money, quantities, IDs, sequence numbers** — silent wrap = corrupted business data. - **Parsing/narrowing untrusted input** — `Math.toIntExact` when a long must become an int. **Don't reach for them when:** - You've **proven** the range can't overflow and the code is a hot path — the per-op check adds a branch. - You actually **want** modular/wrapping behavior (hashing, ring buffers, checksums) — there plain `+` is correct. - The values are **truly unbounded** — use `BigInteger` (arbitrary precision) rather than fighting int limits. ## How they relate to alternatives - **Plain operators:** fast, silent wrap. Default; correct only when wrap is acceptable or impossible. - **`Math.*Exact`:** stay in int/long, fail loudly on overflow. Best for "this should fit, and if it doesn't I want to know." - **Wider type (long):** more headroom but can still overflow. - **`BigInteger`:** no overflow ever, at the cost of heap objects and slower math. - **`Math.floorDiv`/`Math.floorMod`:** related correctness helpers for division/modulo with negatives (not overflow per se). ## Key takeaway `Math.*Exact` = the same arithmetic, but it **throws instead of wrapping**. It is the cheap, in-place way to make integer overflow a detected error rather than a silent corruption.

  • Why is Math.negateExact(Integer.MIN_VALUE) a problem case worth a dedicated method?
    Because -Integer.MIN_VALUE has no representable positive value (the range is asymmetric), so plain negation silently wraps back to MIN_VALUE; negateExact throws instead.
  • What exception type do the *Exact methods throw, and is it checked?
    ArithmeticException, which is unchecked (a RuntimeException), so the compiler does not require handling it.

saying these in an interview costs you the question

  • Thinking *Exact also prevents overflow in normal + operations elsewhere (it only affects that call)
  • Believing ArithmeticException is checked and forces a try/catch (it's unchecked)
  • Claiming they make the value 'saturate' to MAX/MIN (they throw, not clamp)
  • Using them in every arithmetic op including hot loops where wrap is impossible (needless overhead)

context