Skip to content

Bitwise Calculator — Python source

Perform AND, OR, XOR, NOT, shifts and rotates on 8/16/32/64-bit values with exact bigint math. Enter operands in binary, octal, decimal or hex and read the result in every base plus a live bit grid. Runs 100% in your browser.

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

# =============================================================================
#  bitwise.py — CosmoDev polyglot showcase port of the `bitwise` tool
# -----------------------------------------------------------------------------
#  Language : Python 3.10+
#  Source   : ported from src/lib/bitwise.ts (the canonical, live TypeScript lib)
#  License  : display source — part of CosmoDev's polyglot tool pages
#  (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/PHP ports.
# -----------------------------------------------------------------------------
#  Pure, deterministic bitwise calculator. Zero deps (standard library only).
#  Python's `int` is arbitrary precision, so this is a near-direct port of the
#  reference TypeScript; results are exact across all supported widths
#  (8/16/32/64-bit). Operands are interpreted as width-bit two's-complement
#  values: any integer is normalized to the half-open range [0, 2^width) before
#  an operation, and every result is masked back into that range — so the
#  returned int is always the unsigned bit-pattern of the width-bit result.
# =============================================================================

from __future__ import annotations
from typing import Literal

# Numeric base for parsing and formatting bit patterns.
Base = Literal["bin", "oct", "dec", "hex"]

# Supported bitwise operation. `not` is unary on `a`; the binary ops take `a`
# and `b`; for the shift/rotate ops `b` is the count.
Op = Literal["and", "or", "xor", "not", "shl", "shr", "rol", "ror"]

# Operating field width, in bits.
Width = Literal[8, 16, 32, 64]

# Radix per base, consumed by the manual digit parser/formatter below.
_BASE_RADIX: dict[Base, int] = {"bin": 2, "oct": 8, "dec": 10, "hex": 16}

# Lowercase digit alphabet valid for each base — drives validation and the
# per-character digit-value lookup.
_BASE_DIGITS: dict[Base, str] = {
    "bin": "01",
    "oct": "01234567",
    "dec": "0123456789",
    "hex": "0123456789abcdef",
}


def parse(value: str, base: Base) -> int:
    """Parse a numeric string in `base` into an int.

    Accepts an optional leading sign and a single optional base prefix
    (0x/0b/0o, case-insensitive). Raises ValueError on empty input or any digit
    invalid for the requested base. The raw signed value is returned (no width
    normalization); callers fold it into a field via normalize()/bitwise().
    """
    trimmed = value.strip()
    if trimmed in ("", "-"):
        raise ValueError(f"Empty {base} value")

    # Peel off an optional leading '-' so negative literals parse correctly.
    sign = 1
    body = trimmed
    if body[0] == "-":
        sign = -1
        body = body[1:]

    # Strip each base prefix in turn (0x, then 0b, then 0o), case-insensitive —
    # mirrors the reference's chained leading-prefix removal.
    for prefix in ("0x", "0b", "0o"):
        if body[: len(prefix)].lower() == prefix:
            body = body[len(prefix) :]

    if body == "":
        raise ValueError(f"Empty {base} value")

    # Horner's method over the digit alphabet: one pass, exact for any length.
    alphabet = _BASE_DIGITS[base]
    radix = _BASE_RADIX[base]
    acc = 0
    for ch in body.lower():
        digit = alphabet.find(ch)
        if digit < 0:
            raise ValueError(f"Invalid digit {ch!r} for base {base}")
        acc = acc * radix + digit
    return sign * acc


def _mask(width: Width) -> int:
    """Bitmask for a `width`-bit field: 2**width - 1. (Internal helper.)"""
    return (1 << width) - 1


def normalize(n: int, width: Width) -> int:
    """Fold `n` into its unsigned `width`-bit two's-complement value.

    The double-modulo `((n % m) + m) % m` maps negative dividends into the
    canonical unsigned range [0, 2**width) — e.g. -1 at width 8 yields 255.
    (Python's `%` is already floored, but the double form is kept for parity
    with the reference algorithm and the other language ports.)
    """
    m = 1 << width
    return ((n % m) + m) % m


def format(n: int, base: Base, min_digits: int) -> str:
    """Render `n` in `base`, zero-padded to at least `min_digits` places.

    Negative values carry a leading '-' and format their magnitude. `min_digits`
    corresponds to the `width` parameter in the TypeScript reference (a width-bit
    binary value needs exactly `width` digits).

    Note: shadows the builtin ``format`` within this module, matching the TS
    lib's exported ``format`` for API parity across the polyglot ports.
    """
    if n < 0:
        return "-" + format(-n, base, min_digits)
    radix = _BASE_RADIX[base]
    digits = "0" if n == 0 else _to_base(n, radix)
    return digits.zfill(min_digits)


def _to_base(n: int, radix: int) -> str:
    """Convert a non-negative int to a lowercase string in `radix` (2..16)."""
    alphabet = "0123456789abcdef"
    out = []
    while n > 0:
        out.append(alphabet[n % radix])
        n //= radix
    return "".join(reversed(out))


def bitwise(op: Op, a: int, b: int, width: Width) -> int:
    """Apply a `width`-bit bitwise operation.

    Both operands are normalized to `width` bits first, and the result is masked
    back into range — so the returned int is always the unsigned bit-pattern of
    the `width`-bit result.
    """
    m = _mask(width)
    x = normalize(a, width)
    y = normalize(b, width)

    if op == "and":
        return x & y
    if op == "or":
        return x | y
    if op == "xor":
        return x ^ y
    if op == "not":
        return (~x) & m
    if op == "shl":
        # Left shift grows; for shift >= width the masked result is 0.
        # Short-circuit to avoid building a (potentially huge) intermediate.
        return 0 if y >= width else ((x << y) & m)
    if op == "shr":
        # x is normalized non-negative → logical (zero-filling) shift.
        return 0 if y >= width else (x >> y)
    if op in ("rol", "ror"):
        shift = y % width  # rotate amount wraps within width
        if shift == 0:
            return x
        # A right-rotate by `shift` is a left-rotate by (width - shift).
        s = shift if op == "rol" else width - shift
        return ((x << s) | (x >> (width - s))) & m
    raise ValueError(f"Unknown bitwise operation: {op!r}")


def to_bits(n: int, width: Width) -> str:
    """Fixed-width binary string of `width` bits (MSB first)."""
    return format(normalize(n, width), "bin", width)


def flags(n: int, width: Width) -> list[int]:
    """Indices of set bits in the normalized `width`-bit pattern (LSB = 0)."""
    bits = normalize(n, width)
    out = []
    i = 0
    while bits > 0:
        if bits & 1:
            out.append(i)
        bits >>= 1
        i += 1
    return out

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 →