Skip to content

Number Base Converter — Kotlin source

Convert numbers between binary, octal, decimal and hexadecimal. BigInt-powered, so it handles arbitrarily large values without precision loss.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// number-base — Kotlin port: arbitrary-precision base conversion (bases 2-36).
import java.math.BigInteger

private const val DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"

/** Map a lowercase character to its value; -1 when not a digit. */
private fun digitValue(c: Char): Int = when (c) {
    in '0'..'9' -> c - '0'
    in 'a'..'z' -> c - 'a' + 10
    else -> -1
}

/** Parse [s] in [base] (2-36); null on invalid input. A radix prefix is
 *  stripped only when it agrees with the base — so "0xFF" parses in base 16
 *  but is rejected in base 10. */
fun parseBigInt(s: String, base: Int): BigInteger? {
    if (base !in 2..36) return null
    var t = s.trim().lowercase()
    var neg = false
    when {
        t.startsWith("-") -> { neg = true; t = t.substring(1) }
        t.startsWith("+") -> t = t.substring(1)
    }
    if ((t.startsWith("0x") && base == 16) || (t.startsWith("0b") && base == 2) ||
        (t.startsWith("0o") && base == 8)) t = t.substring(2)
    if (t.isEmpty()) return null
    var result = BigInteger.ZERO
    val b = base.toBigInteger()
    for (c in t) {
        val d = digitValue(c)
        if (d < 0 || d >= base) return null
        result = result * b + d.toBigInteger() // Horner's method
    }
    return if (neg) -result else result
}

/** Render [value] in [base] (2-36); "" for an unsupported base, matching the
 *  TS lib. The sign of zero is never emitted. */
fun formatBigInt(value: BigInteger, base: Int): String {
    if (base !in 2..36) return ""
    if (value.signum() == 0) return "0"
    var v = value.abs()
    val b = base.toBigInteger()
    val out = StringBuilder()
    while (v.signum() > 0) {
        val qr = v.divideAndRemainder(b) // peel least-significant digits first
        out.append(DIGITS[qr[1].toInt()])
        v = qr[0]
    }
    if (value.signum() < 0) out.append('-')
    return out.reverse().toString()
}

/** value([fromBase]) -> [toBase]; null when it does not parse. */
fun convertBase(value: String, fromBase: Int, toBase: Int): String? =
    parseBigInt(value, fromBase)?.let { formatBigInt(it, toBase) }

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →