Kotlin 멀티플랫폼 ⚠️ 제로 분할 트랩

작성자

카테고리:

← 피드로
DEV Community · Kotools · 2026-06-13 개발(SW)

Kotools

If you write Kotlin Multiplatform code that involves integer division, you may have already hit this: the exact same expression behaves completely differently depending on which platform compiles it.

🐛 The problem

Take this innocuous expression:

val quotient = 12 / 0
val remainder = 12 % 0

Enter fullscreen mode Exit fullscreen mode

On JVM and Native, both lines throw an ArithmeticException. That is the behavior most Kotlin developers expect and design around.

On JavaScript, both lines execute without any exception and silently return 0.

Here is a concrete illustration drawn directly from the Kotlin test suites for each platform:

// Kotlin/JS
check(12 / 0 == 0)   // passes — no exception
check(12 % 0 == 0)   // passes — no exception

// Kotlin/JVM and Kotlin/Native
val quotient: Result<Int> = runCatching { 12 / 0 }
val remainder: Result<Int> = runCatching { 12 % 0 }
check(quotient.exceptionOrNull() is ArithmeticException)   // passes
check(remainder.exceptionOrNull() is ArithmeticException)  // passes

Enter fullscreen mode Exit fullscreen mode

Summary table:

Expression JVM / Native JavaScript 12 / 0 ArithmeticException 0 12 % 0 ArithmeticException 0

🤔 Why it happens

On Kotlin/JS, Int values are represented as JavaScript numbers, and 12 / 0 evaluates to Infinity while 12 % 0 evaluates to NaN. Kotlin/JS truncates Int arithmetic to 32 bits using JavaScript’s | 0 operator, and per the ECMAScript ToInt32 conversion, both Infinity | 0 and NaN | 0 evaluate to 0 — so the division-by-zero result silently becomes 0, with no exception thrown.

JVM and Native follow Java’s long-standing contract: integer division by zero is always an ArithmeticException.

The practical consequence is that any guard you write and test on JVM — a try/catch(ArithmeticException) or a pre-condition check that relies on an exception — is silently bypassed when the same code runs on JS. No compile error, no warning, just a wrong result.

✅ The fix: Integer from Kotools Types 5.1.1

The Integer type in Kotools Types explicitly checks for a zero divisor before delegating to the platform, so both div and rem throw ArithmeticException consistently on JVM, JavaScript, and Native.

@OptIn(ExperimentalKotoolsTypesApi::class)
fun main() {
    val a: Integer = Integer.from(12)
    val b: Integer = Integer.from(0)

    a / b   // ArithmeticException on JVM, JS, and Native
    a % b   // ArithmeticException on JVM, JS, and Native
}

Enter fullscreen mode Exit fullscreen mode

If you prefer to avoid exception handling altogether, divOrNull and
remOrNull return null instead of throwing:

@OptIn(ExperimentalKotoolsTypesApi::class)
fun main() {
    val a: Integer = Integer.from(12)
    val b: Integer = Integer.from(0)

    val quotient: Integer? = a.divOrNull(b)   // null — no exception
    val remainder: Integer? = a.remOrNull(b)  // null — no exception
}

Enter fullscreen mode Exit fullscreen mode

Integer is annotated with @ExperimentalKotoolsTypesApi and will be stabilized in a future release.

See also: API reference

🛠️ Adding Kotools Types to your project

// build.gradle.kts
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("org.kotools.types:types:5.1.1")
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Opt in either per call-site with @OptIn(ExperimentalKotoolsTypesApi::class) or project-wide via the compiler argument:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-opt-in=org.kotools.types.ExperimentalKotoolsTypesApi")
    }
}

Enter fullscreen mode Exit fullscreen mode

See also: GitHub

💬 Discussion

Have you run into this inconsistency in a real project? How do you currently guard against division by zero in your multiplatform code?

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다