The full picture
Math.abs(Integer.MIN_VALUE) // -2147483648 (still negative!)
Integer.MAX_VALUE + 1 // Integer.MIN_VALUE — wrap, no exception
int mid = (low + high) / 2; // overflows for low+high > 2^31-1
int mid = low + (high - low) / 2; // classic fix
int mid = (low + high) >>> 1; // JDK's fix: unsigned shift
// The senior toolkit:
Math.addExact(a, b) // throws ArithmeticException on overflow
Math.toIntExact(longValue) // narrowing with a guard
Long.compareUnsigned(a, b) // unsigned views without an unsigned type- Why two's complement beat sign-magnitude/ones' complement: single zero (no +0/−0), and addition is the same wires regardless of sign — the hardware-simplicity argument that decided it, worth stating as such.
- Sign extension and narrowing:
(byte) 200→ −56; widening a negative byte drags the sign bit — the source of 'why is my parsed byte negative' bugs in binary protocol code; mask with& 0xFFwhen treating bytes as unsigned. - Language contrast for depth: Java defines wrap; C makes signed overflow undefined (compilers delete your overflow checks!); Rust panics in debug and wraps explicitly (
wrapping_add) in release — three philosophies about the same circuit. - Hash-adjacent trivia that connects:
Integer.MIN_VALUE's asymmetry is why careful code writesx == Integer.MIN_VALUEguards in abs/negate paths (seeMath.negateExact).