What happens on Int overflow in Kotlin arithmetic, and why doesn't `val total: Long = bigInt * bigInt` save you?
answer
- Int overflow wraps silently (two's complement), no exception
- MAX_VALUE + 1 == MIN_VALUE
- Target type doesn't change operand-type arithmetic
- Promote before op: a.toLong() * b
- Math.multiplyExact/addExact throw; int / 0 throws too
basics
~20 sInt math silently wraps around when it gets too big — no error. Assigning to a Long doesn't help, because the multiply already happened as Int and overflowed before the result became a Long. Make an operand a Long first.
solid answer
~40 sKotlin's `Int` arithmetic (+, -, *) follows two's-complement **wrap-around** on overflow — no exception is thrown, you silently get a wrong value (Int.MAX_VALUE + 1 == Int.MIN_VALUE). Because Kotlin never implicitly widens, `val total: Long = a * b` with two Ints computes `a * b` in **Int** first (possibly overflowing) and only then widens the already-wrong result to Long. To compute in Long you must promote an operand before the operation: `a.toLong() * b`. Kotlin provides no checked-arithmetic operators; for overflow detection use `Math.multiplyExact` / `Math.addExact` (which throw `ArithmeticException`), or use `Long`/`BigInteger` from the start. The same wrap-around applies to `Long` at its own boundary; `Double`/`Float` instead saturate to `Infinity`/`-Infinity` and produce `NaN` for 0.0/0.0.
code
kotlin · 10 linesfun main() {
val a = 100_000
val b = 100_000
val bug: Long = a * b // 1410065408 (Int overflow, then widened)
val ok: Long = a.toLong() * b // 10000000000 (Long arithmetic)
println(bug)
println(ok)
println(Int.MAX_VALUE + 1) // -2147483648 (wraps, no exception)
// println(1 / 0) // ArithmeticException: / by zero
}go deeper
Knows Int has a max and big multiplications can go wrong.
Explains silent wrap-around and that you must convert an operand to Long before multiplying.
Distinguishes overflow (silent) from div-by-zero (throws), knows Math.*Exact and the no-implicit-widening evaluation order.
Weighs domain strategies (pick widths up front, BigInteger, checked arithmetic) and articulates the safety trade-offs Kotlin made by not auto-widening.
## Integer overflow wraps silently Kotlin's integer types (`Int`, `Long`, etc.) use fixed-width **two's-complement** representation. Arithmetic that exceeds the range **wraps around** with **no exception**: ```kotlin Int.MAX_VALUE + 1 // -2147483648 (== Int.MIN_VALUE) Int.MIN_VALUE - 1 // 2147483647 ``` This is a frequent source of subtle bugs in counters, hashing, and size calculations. ## Why widening the target doesn't help Kotlin performs **no implicit numeric widening**. The expression on the right of an assignment is evaluated using the **operand** types, independent of the declared variable type: ```kotlin val a = 100_000 val b = 100_000 val total: Long = a * b // BUG: a * b is Int math -> overflows -> wrong value widened to Long println(total) // 1410065408, NOT 10000000000 ``` The multiplication is `Int * Int = Int`, overflows, and only the broken `Int` result is widened to `Long`. Fix by promoting **before** the operation: ```kotlin val total: Long = a.toLong() * b // Long * Int -> Long math -> 10000000000 ``` One `Long` operand pulls the whole expression into `Long` arithmetic. ## Checked arithmetic Kotlin has **no** built-in checked operators. Options when overflow must be caught: - `Math.addExact(a, b)`, `Math.multiplyExact(a, b)`, `Math.subtractExact`, `Math.incrementExact` — throw `ArithmeticException` on overflow (JVM only). - Start in a wider type: `Long`, or `java.math.BigInteger` for unbounded values. ```kotlin try { Math.multiplyExact(a, b) } catch (e: ArithmeticException) { // handle overflow explicitly } ``` ## Floating-point differs `Double`/`Float` do **not** wrap. They saturate: - `1.0 / 0.0` -> `Double.POSITIVE_INFINITY` - `-1.0 / 0.0` -> `Double.NEGATIVE_INFINITY` - `0.0 / 0.0` -> `Double.NaN` Note `0 / 0` with **Int** operands throws `ArithmeticException: / by zero` — integer division by zero **does** throw, unlike overflow which is silent. ## Unsigned types Kotlin also has `UInt`/`ULong` (and `UByte`/`UShort`); they wrap modulo 2^n as well and are handy for bit-pattern work, but they don't add overflow checking. ## Takeaway Choose the right width up front, promote operands before the operation (not at assignment), and reach for `Math.*Exact` or `BigInteger` when correctness under overflow matters.
- Does Int division by zero also wrap silently?No. Integer division (or %) by zero throws ArithmeticException: / by zero. Only overflow is silent; division by zero is an exception.
- How do floating-point types behave at their limits?They saturate, not wrap: overflow gives +/-Infinity, and 0.0/0.0 gives NaN. NaN is never equal to anything, including itself.
saying these in an interview costs you the question
- Expecting an exception on Int overflow
- Believing val x: Long = intA * intB computes in Long
- Thinking Kotlin auto-widens to prevent overflow
- Confusing Int division-by-zero (throws) with overflow (silent)
- Not knowing about Math.*Exact or BigInteger