skip to content

Walk through kotlin.math as the multiplatform alternative to java.lang.Math, including how it handles integer rounding, NaN, and division edge cases.

level: seniorimportance: should knowfreq 35%

answer

  1. kotlin.math replaces java.lang.Math in common
  2. NaN is unordered: use isNaN(), not == NaN
  3. toInt() truncates & NaN.toInt()==0; roundToInt throws on NaN
  4. Int div truncates, /0 throws; float /0 = Infinity
  5. % keeps dividend sign; mod keeps divisor sign

basics

~20 s

kotlin.math gives you sqrt, sin, pow, abs, PI and so on for every platform, so commonMain doesn't need java.lang.Math. It follows IEEE-754: things like 0.0/0.0 give NaN, and there are safe rounding helpers like roundToInt.

solid answer

~40 s

`kotlin.math` is the **common** math package, the multiplatform stand-in for `java.lang.Math`. It exposes trig (`sin`, `cos`, `tan`, `atan2`), `sqrt`/`cbrt`, `pow`, `ln`/`log10`/`log2`, `exp`, `abs`, `min`/`max`, `sign`, `floor`/`ceil`/`round`/`truncate`, `hypot`, and constants `PI`, `E`. Floating point follows IEEE-754: `0.0/0.0` and `sqrt(-1.0)` produce `Double.NaN`; `1.0/0.0` is `Double.POSITIVE_INFINITY`. NaN is unordered, so use `x.isNaN()` rather than `x == Double.NaN` (which is always false). Kotlin adds safe conversions absent from java.lang.Math: `roundToInt()`/`roundToLong()` (which throw on NaN/overflow instead of silently truncating), `Double.toInt()` truncates toward zero. **Integer division** of `Int`/`Long` truncates toward zero and `0` divisor throws `ArithmeticException`, while floating division never throws. `(-7).mod(3)` gives a non-negative result whereas `%` keeps the dividend's sign.

code

kotlin · 8 lines
kotlin
import kotlin.math.roundToInt

fun safePercent(value: Double, total: Double): Int {
    if (total == 0.0) return 0
    val pct = value / total * 100
    require(!pct.isNaN()) { "unexpected NaN" }
    return pct.roundToInt()   // fail-fast on NaN, unlike toInt()
}

go deeper

for a junior

Knows kotlin.math exists and can call sqrt/abs/PI from shared code.

for a middle

Uses roundToInt vs toInt correctly and knows NaN/Infinity are produced, not thrown.

for a senior

Explains IEEE-754 unordered NaN, % vs mod signs, and the collection-equality vs operator-equality nuance.

for a principal

Audits numeric code for portability and correctness, choosing fail-fast conversions and consistent rounding policy across the codebase.

## kotlin.math: the portable math package Because `commonMain` cannot see `java.lang.Math`, Kotlin ships `kotlin.math`, available on all targets: - **Trig**: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2(y, x)`. - **Powers/roots**: `sqrt`, `cbrt`, `pow(exp)`, `hypot(x, y)`. - **Logs/exp**: `ln`, `log10`, `log2`, `log(x, base)`, `exp`. - **Rounding**: `floor`, `ceil`, `round` (half-to-even / banker's rounding for `round`), `truncate`. - **Misc**: `abs`, `min`, `max`, `sign`, constants `PI`, `E`. ## IEEE-754 behavior Floating-point math never throws; it produces special values: ```kotlin import kotlin.math.sqrt println(0.0 / 0.0) // NaN println(sqrt(-1.0)) // NaN println(1.0 / 0.0) // Infinity println(-1.0 / 0.0) // -Infinity ``` **NaN is unordered**: every comparison with NaN is false, including `NaN == NaN`. So test with `x.isNaN()`, `x.isInfinite()`, `x.isFinite()`. Note Kotlin's *structural* equality on boxed `Double?` and in collections treats `NaN == NaN` as true and `-0.0 != 0.0` (a deliberate total ordering for set/map keys), which differs from the IEEE `==` operator on primitives. ## Safe rounding/conversions Kotlin adds helpers java.lang.Math lacks: - `Double.roundToInt()` / `roundToLong()` — round half-up, but **throw** `IllegalArgumentException` on `NaN` and clamp/overflow rules apply; far safer than a silent truncation. - `Double.toInt()` — truncates toward zero, and `NaN.toInt()` yields `0` (a classic trap). ```kotlin val x = Double.NaN x.toInt() // 0 (silent!) // x.roundToInt() // throws IllegalArgumentException — fail-fast ``` ## Integer vs floating division - `Int`/`Long` division **truncates toward zero**; dividing by `0` throws `ArithmeticException`. - `%` (rem) follows the **dividend's sign**: `(-7) % 3 == -1`. - `mod` always returns a result with the **divisor's sign**: `(-7).mod(3) == 2`. Pick `mod` for wrap-around indexing. ## Why this matters in KMP Using `kotlin.math` keeps numeric code in `commonMain` and gives **identical IEEE-754 semantics across platforms** — a major reason shared simulation/finance code can live in common.

  • Why is `x == Double.NaN` always false, and what should you write instead?
    IEEE-754 defines NaN as unequal to everything including itself; use x.isNaN().
  • When would you choose mod over %?
    For wrap-around/index math with negatives — mod always returns a non-negative result for a positive divisor, while % keeps the dividend's sign.
  • Does floating-point division ever throw in Kotlin?
    No — Double/Float division produces Infinity or NaN; only integer division by zero throws ArithmeticException.

kotlin.math is a universal power adapter for math — same calculations plug into every platform socket with identical results.

saying these in an interview costs you the question

  • Checks for NaN with == Double.NaN
  • Assumes Double division by zero throws
  • Thinks toInt() rounds rather than truncates
  • Confuses % and mod sign behavior
  • Reaches for java.lang.Math in commonMain

context