Skip to content

Number Base Converter — Python source

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

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

"""number-base — Arbitrary-precision base conversion.

Language: Python (3.10+).

CosmoDev polyglot showcase port of the ``number-base`` tool, ported from
src/lib/numberBase.ts. Display source — part of CosmoDev's polyglot tool pages.

Python's built-in ``int`` is arbitrary precision, so conversions are exact for
inputs of any length — the same property TypeScript gets from BigInt. Bases
2–36 are supported. Standard library only; no crypto is involved.
"""

DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"


def _digit_value(ch: str) -> int:
    """Map a lowercase character to its numeric value: 0–9 for '0'–'9', 10–35
    for 'a'–'z'. Returns -1 when the character is not a valid digit."""
    if "0" <= ch <= "9":
        return ord(ch) - 48
    if "a" <= ch <= "z":
        return ord(ch) - 87
    return -1


def parse_bigint(text: str, base: int) -> int | None:
    """Parse ``text`` as an integer written in ``base`` (2–36).

    Returns the parsed int, or None when the base is out of range or the input
    contains an invalid digit. A leading sign is honored, and a matching radix
    prefix (``0x``/``0b``/``0o``) is stripped only when it agrees with ``base``.
    """
    if base < 2 or base > 36:
        return None

    s = text.strip().lower()
    negative = False
    if s.startswith("-"):
        negative = True
        s = s[1:]
    elif s.startswith("+"):
        s = s[1:]

    # A 0x/0b/0o prefix is meaningful only for the matching base.
    if (base == 16 and s.startswith("0x")) or \
       (base == 2 and s.startswith("0b")) or \
       (base == 8 and s.startswith("0o")):
        s = s[2:]
    if not s:
        return None

    # Horner's method over the digits. Python's int grows as needed, so this
    # stays exact no matter how long the input.
    result = 0
    for ch in s:
        d = _digit_value(ch)
        if d < 0 or d >= base:
            return None
        result = result * base + d
    return -result if negative else result


def format_bigint(value: int, base: int) -> str:
    """Format ``value`` as a lowercase string in ``base`` (2–36).

    Returns the empty string for an out-of-range base; the sign of zero is
    never emitted.
    """
    if base < 2 or base > 36:
        return ""
    if value == 0:
        return "0"

    negative = value < 0
    v = -value if negative else value
    digits = []
    while v > 0:
        # divmod returns both quotient and remainder in one step, avoiding a
        # second division to recover the digit.
        v, rem = divmod(v, base)
        digits.append(DIGITS[rem])
    # Digits were produced least-significant first; reverse for display.
    if negative:
        digits.append("-")
    return "".join(reversed(digits))


def convert_base(value: str, from_base: int, to_base: int) -> str | None:
    """Parse ``value`` in ``from_base`` and re-emit it in ``to_base``.

    Returns None when the input cannot be parsed.
    """
    n = parse_bigint(value, from_base)
    if n is None:
        return None
    return format_bigint(n, to_base)

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 →