Numeric correctness in Java
Silent numeric bugs cause more wrong answers than logic errors: int overflow, Math.abs of the minimum value, signed shifts, negative modulo and floating-point equality. Each item here has a concrete failing input and the fix.
Learning goals
- Predict and prevent int overflow
- Compute midpoints and products safely
- Use floorMod and shifts correctly
- Compare floating-point values with a tolerance
Prerequisites
int vs long
An int holds up to 2,147,483,647 (about 2.1 × 10⁹). Values such as sums of 10⁵ numbers each up to 10⁵, products of two 10⁵ values, or factorials above 12! no longer fit. Use long (up to about 9.2 × 10¹⁸) whenever an intermediate result may exceed 2³¹ - 1.
Pick the type for the largest intermediate value, not the largest input. Open the demo and try different operations.
1int sumInt = 0; long sumLong = 0;2for (int i = 0; i < 100_000; i++) { sumInt += i; sumLong += i; }3// sumLong = 4999950000; sumInt wrapped around to 7049827044long fact = 1; for (int i = 2; i <= 20; i++) fact *= i; // 20! = 2432902008176640000 fits in long
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
Integer overflow
Java integer arithmetic wraps around silently modulo 2³²: Integer.MAX_VALUE + 1 is Integer.MIN_VALUE. No exception is thrown, so the program continues with a wrong value. Detect it with Math.addExact, subtractExact and multiplyExact (they throw ArithmeticException) or by computing in long.
Step through the demo's addition cases to see the wrapped results.
1int x = Integer.MAX_VALUE; // 21474836472int y = x + 1; // -21474836483try { Math.addExact(x, 1); } catch (ArithmeticException e) { System.out.println("overflow detected"); }4boolean willOverflow = (long) x + 1 > Integer.MAX_VALUE; // true
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
Safe midpoint
(lo + hi) / 2 overflows when lo + hi exceeds 2³¹ - 1. With lo = 1_500_000_000 and hi = 2_000_000_000 the sum wraps to -794,967,296 and the midpoint becomes -397,483,648, a negative index. The safe forms are lo + (hi - lo) / 2 or (lo + hi) >>> 1.
This bug lived in the JDK's own binary search for years. Always use the safe form.
1int lo = 1_500_000_000, hi = 2_000_000_000;2int bad = (lo + hi) / 2; // -3974836483int good = lo + (hi - lo) / 2; // 17500000004int also = (lo + hi) >>> 1; // 1750000000 (unsigned shift treats the wrapped sum as unsigned)
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
long multiplication
Multiplying two ints gives an int, and it overflows before any widening to long happens. long p = a * b; is wrong for large ints. Cast one operand first: long p = (long) a * b;.
For modular arithmetic, reduce after every multiplication and make sure the product of two reduced values fits in long. With modulus 10⁹ + 7 the product is below 10¹⁸ + 10¹⁰, which fits.
1int a = 100_000, b = 100_000;2long wrong = a * b; // 1410065408 (int multiplication, then widened)3long right = (long) a * b; // 100000000004final int MOD = 1_000_000_007;5long r = ((long) 123_456_789 * 987_654_321) % MOD;
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
Math.abs corner case
Math.abs(Integer.MIN_VALUE) returns Integer.MIN_VALUE (still negative) because +2,147,483,648 does not fit in an int. The same applies to Long.MIN_VALUE, and -x and Math.abs on a hash code can produce a negative array index.
Use Math.floorMod(hash, n) for bucket indexes, and use long when you must take absolute values of arbitrary ints.
1int a = Math.abs(Integer.MIN_VALUE); // -21474836482long b = Math.abs((long) Integer.MIN_VALUE); // 21474836483int bucket = Math.floorMod("polygenelubricants".hashCode(), 16); // always 0..15
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
Signed shifts
x << k shifts left, filling with zeros (multiplying by 2ᵏ unless it overflows). x >> k is an arithmetic shift: it copies the sign bit, so negative values stay negative and the result rounds toward negative infinity. x >>> k is a logical shift that fills with zeros, treating the 32 bits as unsigned.
-8 >> 1 is -4, but -8 >>> 1 is 2147483644. For ints the shift distance is taken modulo 32, so 1 << 32 equals 1.
1int a = -8 >> 1; // -42int b = -8 >>> 1; // 21474836443int c = 1 << 31; // -21474836484int d = 1 << 32; // 1 (shift distance 32 % 32 = 0)5int half = -7 >> 1; // -4: floors; -7 / 2 is -3 (truncates)
32-bit int arithmetic and overflow
Interactive · edit the inputa = 2147483647
b = 1
A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit.
1int a = ..., b = ...; // 32-bit signed: -2147483648 .. 21474836472int sum = a + b; // wraps around silently on overflow3long wide = (long) a + b; // widen BEFORE the operation4int prod = a * b; // wraps around silently on overflow5long wideProd = (long) a * b; // widen one operand first6int unsafeMid = (lo + hi) / 2; // lo + hi may overflow and turn negative7int safeMid = lo + (hi - lo) / 2; // hi - lo cannot overflow when 0 <= lo <= hi8int abs = Math.abs(a); // Math.abs(Integer.MIN_VALUE) is still negative9int arith = a >> b; // shifts in copies of the sign bit10int logical = a >>> b; // shifts in zeros
Live state
a
2147483647
b
1
Execution trace (dry run table)Show 3 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | a=2147483647 b=1 | A Java int is 32 bits in two's complement. Here are the operands as bit patterns; the top bit is the sign bit. |
| 2 | L2 | true result=2147483648 int range=[-2147483648, 2147483647] | The true result 2147483648 is outside the int range. |
| 3 | L2 | return=-2147483648 overflow=true | Result: -2147483648. The bits wrapped around instead of raising an error, so the answer is wrong by 2^32 = 4294967296. Java never throws on int overflow (use Math.addExact or long to detect or avoid it). |
Modulo sign
In Java % takes the sign of the dividend, so -7 % 3 is -1, and integer division truncates toward zero, so -7 / 3 is -2. For a non-negative remainder use Math.floorMod, and Math.floorDiv for the matching quotient (-3 here).
A common idiom for modular subtraction is ((a - b) % m + m) % m, or Math.floorMod(a - b, m).
1int r1 = -7 % 3; // -12int r2 = Math.floorMod(-7, 3); // 23int q1 = -7 / 3; // -24int q2 = Math.floorDiv(-7, 3); // -35int wrapIndex = Math.floorMod(0 - 1, 5); // 4: circular previous index
Floating-point comparisons
double stores binary approximations: 0.1 + 0.2 is 0.30000000000000004, so 0.1 + 0.2 == 0.3 is false. Compare with a tolerance, Math.abs(a - b) < 1e-9, or avoid floating point by using integers (cents instead of dollars) or BigDecimal.
Beyond 2⁵³ double cannot represent every integer, so never use it for exact integer arithmetic on large values.
1double s = 0.1 + 0.2;2boolean eq = s == 0.3; // false3boolean close = Math.abs(s - 0.3) < 1e-9; // true4long cents = 10 + 20; // exact: 305int cmp = Double.compare(0.1 + 0.2, 0.3); // 1
Practice ladder
Now try it yourself
Easy
- Predict Integer.MAX_VALUE + 1 and Math.abs(Integer.MIN_VALUE)
- Compute -17 % 5 and Math.floorMod(-17, 5)
Medium
- Fix
long p = a * bfor int a and b - Why can (lo + hi) / 2 fail in a binary search over a 2 × 10⁹ range? Give numbers
Advanced
- Write a method that adds two ints and returns Integer.MAX_VALUE or MIN_VALUE on overflow (saturating addition) without using long
Key takeaways
- Overflow wraps silently: widen to long before the operation, not after.
- Use lo + (hi - lo) / 2 and Math.floorMod; avoid Math.abs on arbitrary ints.
- Use tolerances or integers instead of == on doubles.