Skip to content

Bitwise Calculator — TypeScript 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 TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Pure, deterministic bitwise calculator. Zero deps. Operates on `bigint` so
// results are exact across all supported widths (8/16/32/64-bit). Operands are
// interpreted as width-bit two's-complement values: any bigint 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 bigint is always the unsigned
// bit-pattern of the width-bit result.
// Digit math lives in bigintBaseCore; this module defines the typed-throw
// contract (0x/0b/0o prefixes stripped unconditionally).

import { parseDigits, formatDigits } from './bigintBaseCore';

export type Base = 'bin' | 'oct' | 'dec' | 'hex';
export type Op = 'and' | 'or' | 'xor' | 'not' | 'shl' | 'shr' | 'rol' | 'ror';
export type Width = 8 | 16 | 32 | 64;

const BASE_RADIX: Record<Base, number> = { bin: 2, oct: 8, dec: 10, hex: 16 };

/** Parse a numeric string in `base` into a bigint. Strips 0x/0b/0o prefixes. */
export function parse(value: string, base: Base): bigint {
  const r = parseDigits(value, BASE_RADIX[base], { strictPrefix: false });
  if (r.ok) return r.value;
  if (r.reason === 'empty') throw new Error(`Empty ${base} value`);
  throw new Error(`Invalid digit '${r.ch}' for base ${base}`);
}

/** Mask for a `width`-bit field: 2^width - 1. */
function mask(width: Width): bigint {
  return (1n << BigInt(width)) - 1n;
}

/** Normalize any bigint to its unsigned width-bit two's-complement value. */
export function normalize(n: bigint, width: Width): bigint {
  const m = 1n << BigInt(width);
  return ((n % m) + m) % m;
}

/** Format `n` in `base`, zero-padded to at least `width` digits. Negatives carry a leading '-'. */
export function format(n: bigint, base: Base, width: number): string {
  return formatDigits(n, BASE_RADIX[base], { minDigits: width });
}

/**
 * Apply a width-bit operation. `a` is the (unary) operand for `not`; `b` is the
 * second operand for binary ops and the shift/rotate count for shl/shr/rol/ror.
 * Both operands are normalized to width-bit two's complement first; the result
 * is masked to `width` bits.
 */
export function bitwise(op: Op, a: bigint, b: bigint, width: Width): bigint {
  const m = mask(width);
  const x = normalize(a, width);
  const y = normalize(b, width);

  switch (op) {
    case 'and': return x & y;
    case 'or': return x | y;
    case 'xor': return x ^ y;
    case 'not': return (~x) & m;
    case 'shl': return (x << y) & m;
    case 'shr': return x >> y; // x is normalized non-negative → logical shift
    case 'rol':
    case 'ror': {
      const w = BigInt(width);
      const shift = y % w; // rotate amount wraps within width
      if (shift === 0n) return x;
      const s = op === 'rol' ? shift : w - shift;
      return ((x << s) | (x >> (w - s))) & m;
    }
  }
}

/** Fixed-width binary string of `width` bits (MSB first). */
export function toBits(n: bigint, width: Width): string {
  return format(normalize(n, width), 'bin', width);
}

/** Indices of set bits (LSB = index 0), normalized to `width`. */
export function flags(n: bigint, width: Width): number[] {
  const bits = normalize(n, width);
  const out: number[] = [];
  let i = 0;
  for (let v = bits; v > 0n; v >>= 1n) {
    if (v & 1n) out.push(i);
    i++;
  }
  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 →