Home Java ArithmeticException: Fix Java Divide-by-Zero
Beginner 5 min · September 23, 2026

ArithmeticException: Fix Java Divide-by-Zero

Fix ArithmeticException fast: guard integer divisors, handle doubles separately, and scale every BigDecimal divide with a rounding mode...

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.

Follow
Production
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
Before you start⏱ 8 min
  • Basic Java arithmetic and types
  • BigDecimal basics
  • A JDK to compile examples
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • ArithmeticException means integer division or remainder by zero — int and long only, never floating point
  • Double division by zero yields Infinity or NaN instead of throwing, so check which type you're dividing
  • Guard every integer divisor with a zero check or validation before the operation runs
  • Give BigDecimal.divide an explicit scale and rounding mode or it throws on non-terminating results
✦ Definition~90s read
What is Java ArithmeticException Fix?

ArithmeticException is an unchecked exception in java.lang thrown when an integer arithmetic operation has no defined result — overwhelmingly division or remainder by zero on int or long. 7 / 0 and 7 % 0 throw; so do their long equivalents. It extends RuntimeException, so callers aren't forced to handle it — they're expected to keep divisors nonzero.

Picture splitting a pizza among zero friends — the question makes no sense, and Java refuses to answer it.

You'll meet it in averages, percentages, pagination math, rate calculations, and anywhere a count or size feeds a divisor.

The critical split is integer versus floating-point. Double and float division by zero never throws: 7.0 / 0.0 yields Infinity, 0.0 / 0.0 yields NaN, with signed infinities for negative dividends. Code mixing types follows the wider operand — 7 / 0.0 is double division and returns Infinity.

Guards written for doubles are dead code; missing guards on ints are live bugs. Know each division's type before deciding it needs protection.

BigDecimal adds a third shape: divide() without scale throws ArithmeticException on non-terminating results like 1 / 3, even with nonzero divisors, because exact representation is impossible. The fix is divide(divisor, scale, roundingMode) — explicit precision instead of exactness.

Sibling exact-methods Math.addExact, subtractExact, multiplyExact, and friends throw the same exception on overflow rather than wrapping silently. One exception type, three families: zero divisors, non-terminating precision, and exact-overflow — each with its own guard.

Plain-English First

Picture splitting a pizza among zero friends — the question makes no sense, and Java refuses to answer it. That's ArithmeticException: integer math hitting divide-by-zero and throwing up its hands. But decimals play by looser rules — double division by zero calmly answers Infinity instead of crashing. The fix is checking the guest count before cutting: guard integer divisors, know doubles won't throw, and give precise BigDecimal math its rounding instructions upfront.

The trace says ArithmeticException: / by zero at the line computing an average. The count came from a query that returned no rows, the total divided by that zero count, and the nightly job died. The values were all legitimate — an empty day is normal — but nobody guarded the division, so normal input became a fatal error.

This exception has the sharpest split personality in Java: integer division by zero throws, floating-point division by zero doesn't. Developers who learn one rule apply it to both and get burned — either missing guards on ints or writing dead guards on doubles. Add BigDecimal's divide, which throws for non-terminating decimals even with nonzero divisors, and the family needs a proper map.

This guide draws it. You'll learn the int-versus-double split, divisor guards that fit real code, BigDecimal scale and rounding, empty-dataset averages, and the overflow cousins that throw the same type. By the end, every division in your code carries the right protection for its type — and empty inputs produce defaults or messages, not pages.

Integers Throw, Doubles Don't: the Great Split

Memorize this table and half the confusion vanishes. int and long division or remainder by zero throws ArithmeticException — always, deterministically. float and double division by zero never throws: positive over zero gives Infinity, negative gives -Infinity, zero over zero gives NaN. Mixed expressions follow the wider type, so 7 / 0.0 is double division returning Infinity while 7 / 0 throws. The operator looks identical; the operand types decide everything.

This split bites both directions. Teams guard double divisions that can't throw — harmless dead code that signals misunderstanding. Worse, teams skip guards on integer math assuming doubles' leniency, or convert to double to dodge the throw and inherit Infinity downstream where a price becomes infinite and a comparison silently passes. Infinity propagates through calculations the way null propagates through calls — far and quietly.

The demo below prints every case side by side. Run it once, show it to every junior, and pin it in the team wiki. When reviewing division code, the first question is always the operand types — the answer dictates guard or no guard. When reviewing division code, the first question is always the operand types, because the answer dictates guard or no guard. Run the demo once and pin it in the team wiki for every junior to see.

io/thecodeforge/errors/DivSplit.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class DivSplit {
    public static void main(String[] args) {
        try {
            System.out.println(7 / 0); // throws ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("int 7/0 THROWS: " + e.getMessage());
        }
        System.out.println(7.0 / 0.0);  // Infinity, no throw
        System.out.println(-7.0 / 0.0); // -Infinity, no throw
        System.out.println(0.0 / 0.0);  // NaN, no throw
        System.out.println(7 / 0.0);    // wider type wins: Infinity
    }
}
// Run: javac DivSplit.java && java DivSplit
📊 Production Insight
A price service converted to double to dodge the throw and shipped Infinity prices for empty regions — comparisons passed, totals corrupted. Rule: dodging with doubles trades a loud throw for quiet Infinity propagation.
🎯 Key Takeaway
int/long by zero throws; double by zero yields Infinity or NaN.
Mixed expressions follow the wider operand type.
Review division by asking operand types first, always.

Guarding Divisors in Real Code

Divisor guards belong where the divisor is born, not where the division happens. A count from a query, a size from a list, a user-supplied split count — validate at entry with a message naming the value and rule: split count must be positive, got 0. The division downstream then runs unconditionally on trusted data. Guards at the division site work but scatter; guards at the source centralize and document.

Choose the zero policy per meaning. Averages over empty data return Optional.empty, a no-data response, or a documented default — never a fabricated zero that looks like a real average. Pagination with zero page size throws a validation error because it's a caller bug. Rates with zero elapsed time skip the sample. The code review question is always what zero means here — and the answer differs per divisor.

The snippet shows the helper pattern: one guarded average used by every endpoint, so new callers inherit protection. Centralizing arithmetic with guards beats scattering ifs at every division site. When the next empty region launches, the helper answers gracefully while unguarded code would page. Centralizing arithmetic with guards beats scattering ifs at every division site, especially as new callers arrive. The code review question is always what zero means here, and the answer differs per divisor.

io/thecodeforge/errors/Averages.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.OptionalDouble;

public final class Averages {
    private Averages() {}

    public static OptionalDouble mean(long total, long count) {
        if (count <= 0) {
            return OptionalDouble.empty(); // empty data: honest answer
        }
        return OptionalDouble.of((double) total / count);
    }

    public static int pages(int items, int pageSize) {
        if (pageSize <= 0) {
            throw new IllegalArgumentException("pageSize must be positive, got " + pageSize);
        }
        return (items + pageSize - 1) / pageSize; // safe: divisor verified
    }
}
📊 Production Insight
The pricing outage in this article's story needed one OptionalDouble-shaped guard at the helper — instead every endpoint divided raw counts. Rule: centralize division in guarded helpers; raw / on external counts is a finding.
🎯 Key Takeaway
Validate divisors at their source with named messages.
Empty data gets honest answers — Optional.empty, not fabricated zeros.
Central helpers spread guards to every caller automatically.

BigDecimal Divide Needs Scale and Rounding

BigDecimal's exact divide() throws ArithmeticException on non-terminating results — 1 divided by 3 has no exact decimal form, so exactness is impossible and the method says so loudly. This surprises developers who chose BigDecimal for safety and meet a throw with nonzero divisors. The exception is correct: you asked for exact, exact doesn't exist. The repair is stating acceptable precision: divide(divisor, scale, roundingMode).

Always use the three-argument form for division in money code: amount.divide(BigDecimal.valueOf(3), 2, RoundingMode.HALF_UP) for cents with banker's expectations documented. Choose scale per domain — 2 for currency display, higher for intermediate rates — and HALF_UP unless regulations say otherwise. Construct from strings or valueOf, never from doubles, or binary noise enters before division even runs.

The snippet contrasts the throw with the scaled cure plus the double-constructor trap. Money code review checks three things: string or valueOf construction, three-argument divide, documented rounding. Miss any and the change doesn't merge — precision bugs cost real cents at scale. Money code review checks three things: string construction, three-argument divide, and documented rounding on every call. Miss any of them and the change should not merge, since precision bugs cost real cents.

io/thecodeforge/errors/MoneyDivide.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.math.BigDecimal;
import java.math.RoundingMode;

public final class MoneyDivide {
    public static BigDecimal split(BigDecimal amount, int parts) {
        if (parts <= 0) {
            throw new IllegalArgumentException("parts must be positive, got " + parts);
        }
        return amount.divide(BigDecimal.valueOf(parts), 2, RoundingMode.HALF_UP);
    }

    public static void main(String[] args) {
        try {
            System.out.println(new BigDecimal("1").divide(new BigDecimal("3")));
        } catch (ArithmeticException e) {
            System.out.println("exact divide THROWS: non-terminating");
        }
        System.out.println(split(new BigDecimal("10.00"), 3)); // 3.33
    }
}
⚠ Exact Divide on Non-Terminating Results Always Throws
BigDecimal divide() without scale demands exactness, and 1/3 has no exact decimal form. Use the three-argument divide with scale and RoundingMode in all money code — no exceptions, literally.
📊 Production Insight
A fee splitter used exact divide and threw on every three-way split while two-way splits passed — the bug struck only when math was non-terminating. Rule: grep money code for two-argument divide() calls and fix each one.
🎯 Key Takeaway
Exact divide throws where exactness is impossible — by design.
Three-argument divide with scale and rounding is the money standard.
Construct BigDecimals from strings, never doubles.

Averages, Percentages, and Empty Datasets

Empty inputs are the top production source: zero rows, zero users, zero elapsed milliseconds. Every formula dividing by a count needs its empty path written before launch — not discovered at launch. Revenue per user with no users, error rate with no requests, velocity with no completed points: each is a division whose divisor starts at zero on day one for somebody. New regions, new tenants, and quiet days deliver the zero on schedule.

Percentages hide a second trap: integer percentage math truncates before it divides. done 100 / total works in int when ordered so multiplication precedes division; done / total 100 truncates to zero first for any partial progress. Order operations to preserve precision, or compute in double deliberately. Pagination's ceiling formula (items + size - 1) / size needs the guarded size from section two — its divisor is user-controlled.

The snippet shows empty-safe rate helpers returning honest optionals plus truncation-safe percentage order. Dashboards should distinguish no-data from zero distinctly — a gap versus a flatline tells different stories. Build the distinction into the helper and every chart inherits it. Build the no-data distinction into the helper and every chart inherits honest gaps instead of fake zeros. New regions, new tenants, and quiet days deliver the zero on schedule, so design for it.

io/thecodeforge/errors/Rates.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.OptionalDouble;

public final class Rates {
    private Rates() {}

    public static OptionalDouble errorRate(long errors, long total) {
        if (total <= 0) {
            return OptionalDouble.empty(); // no traffic: gap, not zero
        }
        return OptionalDouble.of((double) errors * 100 / total);
    }

    public static int percent(long done, long total) {
        if (total <= 0) {
            throw new IllegalArgumentException("total must be positive, got " + total);
        }
        return (int) (done * 100 / total); // multiply first: no truncation
    }
}
📊 Production Insight
A progress bar showed 0% for months on partial work because done / total truncated first. Users thought jobs were stuck. Rule: multiply before dividing in integer percentages, or compute in double on purpose.
🎯 Key Takeaway
Empty datasets are day-one inputs for someone — write the empty path first.
Order integer math to preserve precision: multiply, then divide.
Distinguish no-data gaps from true zeros on dashboards.

Remainder, Overflow Cousins, and addExact

The % operator throws on zero exactly like division — 7 % 0 is ArithmeticException, not some gentler error. Guard remainders with the same divisor checks; cyclic math with a period that can be zero (empty rotation, unconfigured interval) needs the same empty-path treatment as averages. Reviewers should read % as / for guard purposes — same family, same rule.

Silent integer overflow is the adjacent trap that doesn't throw: 2_000_000_000 + 1_500_000_000 wraps negative with no complaint, corrupting totals that later divide into nonsense. The Math.*Exact family — addExact, subtractExact, multiplyExact, incrementExact, decrementExact, negateExact — throws ArithmeticException on overflow instead of wrapping. Use them for money totals, counters near limits, and any arithmetic whose overflow would corrupt downstream division.

The snippet shows the remainder guard plus exact-arithmetic detection. Exact methods convert silent corruption into loud failure at the overflow line — the same philosophy as divisor guards. For values that legitimately exceed int range, widen to long deliberately rather than catching overflow as control flow. For values that legitimately exceed int range, widen to long deliberately rather than catching overflow as control flow. Exact methods convert silent corruption into loud failure at the exact overflow line.

io/thecodeforge/errors/RemainderExact.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final class RemainderExact {
    public static int slot(int index, int buckets) {
        if (buckets <= 0) {
            throw new IllegalArgumentException("buckets must be positive, got " + buckets);
        }
        return Math.floorMod(index, buckets); // zero-safe by guard, sign-safe by API
    }

    public static long total(long a, long b) {
        return Math.addExact(a, b); // throws on overflow instead of wrapping
    }

    public static void main(String[] args) {
        System.out.println(slot(7, 3)); // 1
        try {
            System.out.println(Math.addExact(Long.MAX_VALUE, 1));
        } catch (ArithmeticException e) {
            System.out.println("overflow THROWS: " + e.getMessage());
        }
    }
}
📊 Production Insight
A loyalty total wrapped past Integer.MAX_VALUE into negative, then divided into negative points per user. Exact methods would have thrown at the addition. Rule: money and counter arithmetic uses *Exact or long — never silent int.
🎯 Key Takeaway
% by zero throws like / by zero — guard identically.
*Exact methods throw on overflow instead of wrapping silently.
Widen to long deliberately where values can exceed int range.

Reading / by zero and Locking the Fix

The message is terse — / by zero — and the line supplies the rest. Open it, identify the divisor operand, and trace that operand to its producer: a query count, a collection size, a config value, an elapsed-time subtraction. The divisor's story is the bug; the division is just where the story ends. Zero elapsed time from identical timestamps, zero counts from empty results, and zero sizes from fresh tenants cover nearly every production case.

Reproduce with the zero directly — call the method with count zero, run the query against the empty partition, set the clock source identical. If the code handles it gracefully after your fix, the repro becomes the regression test: empty input in, honest empty answer out, no throw. Name the test for the zero-state so history is visible: averageOfNoRowsReturnsEmpty.

Lock the family, not just the instance: grep every / and % on external counts across the service and route them through guarded helpers. One fixed endpoint while five siblings divide raw counts is half an incident waiting. Centralize, test empties per helper, and empty inputs stop paging forever. Centralize, test empties per helper, and empty inputs stop paging forever across the whole service. Fix the family with helpers rather than patching one line with a lone if.

📊 Production Insight
After the pricing outage, a grep found four more raw divisions on query counts in the same service — each a future page. One afternoon centralized them all. Rule: fix the family with helpers, not the instance with an if.
🎯 Key Takeaway
Trace the divisor to its producer — the story, not the line, is the bug.
Repro with zero becomes the regression test for the empty path.
Centralize guarded helpers across every division on external counts.
● Production incidentPOST-MORTEMseverity: high

Zero-Row Query Crashed Pricing for 35 Minutes

Symptom
At 9:02 AM, pricing API error rates jumped to 100% for the newly launched region while all older regions stayed green. Every request threw ArithmeticException: / by zero from the margin calculation. The launch dashboard showed zero successful price quotes for 35 minutes; sales reps refreshed dead pages through the entire morning rush.
Assumption
The team assumed the deploy had broken pricing logic and prepared a full rollback. A rollback would have worked — by restoring code that never served the new region — while teaching nothing. An engineer reproduced with the region's data first: zero sales rows, zero count, division by zero. The code was fine for every region with data; the new region's emptiness was the trigger.
Root cause
The margin average divided revenue by sale count using integer math, and the new region's first-day count was zero — legitimate empty data. No guard checked the divisor because every tested region had sales. The throw escaped per request, and the endpoint had no fallback for the no-data case, so emptiness became a total outage for that region.
Fix
A zero-count guard returning a no-data response was deployed at 9:37 AM, restoring the region immediately. The average helper now validates counts with a named error, dashboards track no-data responses per region, and launch checklists include an empty-region pricing test before any new market goes live.
Key lesson
  • Empty datasets are normal inputs, not edge cases. Every division needs a zero-count path — default, message, or fallback — designed before launch.
  • New regions, tenants, and days start empty. Launch tests must cover the zero-state, not just the populated happy path.
  • Reproduce with the failing data before rolling back. The data pointed at the guard in minutes; the rollback instinct would have hidden the lesson.
Production debug guideFive steps that find the zero feeding the division.5 entries
Symptom · 01
The trace names / by zero — find the divisor
Fix
Open the exact line and identify the divisor expression. Log it just above: System.out.println("count=" + count). Reproduce with zero input: javac AvgRepro.java && java AvgRepro 0. The divisor's producer — query, size call, or counter — is the fix site, not the division.
Symptom · 02
You need to know int versus double at the throw site
Fix
Check operand types with javap -c -p com/example/Pricing.class | grep -E 'idiv|ldiv|ddiv'. idiv or ldiv means integer and throws; ddiv means double and can't throw this. Fix guards only where idiv or ldiv appear.
Symptom · 03
A BigDecimal divide throws on nonzero values
Fix
Look for scale-less divide: grep -rn '\.divide(' src/main/java | grep -v 'RoundingMode'. Non-terminating results like 1/3 need divide(d, scale, RoundingMode.HALF_UP). Confirm with a probe: javac BdRepro.java && java BdRepro.
Symptom · 04
The failure tracks a specific deployed build
Fix
Verify deployed math: jar tf app.jar | grep 'Pricing.class' and strings on the class for divisor defaults. Rebuild with mvn -q clean package or gradle build and rerun with the failing region's data before editing guards.
Symptom · 05
Zero counts recur across endpoints
Fix
Check live counters during failure: jstack $(pgrep -f app.jar) > /tmp/threads.txt confirms request scope, then add zero-count metrics per endpoint. Centralize average helpers with built-in guards so each new endpoint inherits protection.
ArithmeticException Causes Compared
Root CauseHow to ConfirmFixPrevention
Integer division by zero/ by zero with int/long operandsZero-check divisor; honest empty pathGuarded average helpers everywhere
Remainder by zero% by zero; period or bucket count zeroValidate period positive before %Treat % like / in every review
BigDecimal non-terminating divideThrow with nonzero divisor; two-arg divideThree-arg divide with scale and roundingBan two-arg divide in money code
Silent int overflow upstreamWrapped negatives; totals near MAX_VALUEMath.addExact family; widen to long*Exact for money and counters
Double Infinity confusionNo throw but Infinity/NaN downstreamValidate before; check isFinite afterKnow operand types before guarding
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
iothecodeforgeerrorsDivSplit.javapublic final class DivSplit {Integers Throw, Doubles Don't
iothecodeforgeerrorsAverages.javapublic final class Averages {Guarding Divisors in Real Code
iothecodeforgeerrorsMoneyDivide.javapublic final class MoneyDivide {BigDecimal Divide Needs Scale and Rounding
iothecodeforgeerrorsRates.javapublic final class Rates {Averages, Percentages, and Empty Datasets
iothecodeforgeerrorsRemainderExact.javapublic final class RemainderExact {Remainder, Overflow Cousins, and addExact

Key takeaways

1
Integer / and % by zero throw; doubles yield Infinity or NaN.
2
Guard divisors at their source with honest empty paths.
3
BigDecimal needs scale plus rounding on every divide.
4
Order integer percentages to preserve precision.
5
*Exact methods make overflow loud instead of silent.
6
Centralize guarded helpers; fix the family, not one line.

Common mistakes to avoid

6 patterns
×

Dividing raw query counts without guards

Symptom
First empty day, region, or tenant throws per request until someone notices.
Fix
Route all count divisions through guarded helpers returning honest empties for zero counts.
×

Guarding double divisions that can't throw

Symptom
Dead zero-checks on doubles while neighboring integer math stays unguarded.
Fix
Check operand types first with javap if unsure. Guard idiv/ldiv sites; validate double finiteness instead.
×

Converting to double to dodge the throw

Symptom
Infinity prices and NaN rates propagate silently through totals and comparisons.
Fix
Keep integer semantics with explicit empty paths. Validate doubles with Double.isFinite after risky math.
×

Using two-argument BigDecimal divide

Symptom
Throws only on non-terminating splits — passes halves, fails thirds, in production.
Fix
Always divide(amount, scale, RoundingMode.HALF_UP) in money code. Grep and fix every two-arg call.
×

Truncating integer percentages

Symptom
Progress stuck at 0% for partial work; done/total zeroes before multiplying.
Fix
Multiply first (done * 100 / total) or compute in double deliberately with documented rounding.
×

Ignoring silent int overflow

Symptom
Totals wrap negative near MAX_VALUE, then divide into nonsense without any throw.
Fix
Use Math.addExact and siblings for totals and counters, or widen to long where growth is legitimate.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What throws ArithmeticException?
Q02JUNIOR
Why doesn't 7.0 / 0.0 throw?
Q03SENIOR
How do you handle averages over possibly-empty data?
Q04SENIOR
Why does BigDecimal divide throw on nonzero divisors?
Q05SENIOR
When do Math.*Exact methods help?
Q01 of 05JUNIOR

What throws ArithmeticException?

ANSWER
Integer division or remainder by zero on int or long. Floating-point division by zero never throws — it yields Infinity or NaN. BigDecimal exact divide throws on non-terminating results, and Math.*Exact throws on overflow.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Is it checked or unchecked?
02
Can I catch it around the division?
03
Why did my percentage stay zero?
04
How do I check a double result is sane?
05
Which RoundingMode for money?
06
long or BigDecimal for money totals?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.

Follow
Verified
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
🔥

That's Exception Handling. Mark it forged?

5 min read · try the examples if you haven't

Previous
Java ConcurrentModification Fix
19 / 19 · Exception Handling
Next
Android NetworkOnMainThread Fix