Bitwise Calculator — JavaScript 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 JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// =============================================================================
// bitwise.js - CosmoDev polyglot showcase port of the `bitwise` tool
// -----------------------------------------------------------------------------
// Language : JavaScript (ES2020+, runs on BigInt)
// 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 TS/Go/Rust/PHP/Python ports.
// -----------------------------------------------------------------------------
// 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.
// =============================================================================
'use strict';
/** Numeric radix for each supported base. */
const BASE_RADIX = { bin: 2, oct: 8, dec: 10, hex: 16 };
/** Digits valid for each base (lowercased). Used for both validation and parsing. */
const BASE_DIGITS = {
bin: '01',
oct: '01234567',
dec: '0123456789',
hex: '0123456789abcdef',
};
/**
* Parse a numeric string in `base` into a bigint. Strips 0x/0b/0o prefixes and
* an optional leading sign. Throws on empty input or any out-of-base digit.
*/
export function parse(value, base) {
const trimmed = String(value).trim();
if (trimmed === '' || trimmed === '-') {
throw new Error(`Empty ${base} value`);
}
// Peel off an optional leading '-' so negative literals parse correctly.
let sign = 1n;
let body = trimmed;
if (body[0] === '-') {
sign = -1n;
body = body.slice(1);
}
// Strip each base prefix in turn (0x, then 0b, then 0o, case-insensitive),
// applied sequentially - this mirrors the reference's chained replacement.
body = body
.replace(/^0x/i, '')
.replace(/^0b/i, '')
.replace(/^0o/i, '');
if (body === '') throw new Error(`Empty ${base} value`);
// Horner's method over the digit alphabet: one pass, exact for any length.
const allowed = BASE_DIGITS[base];
const radix = BigInt(BASE_RADIX[base]);
let acc = 0n;
for (const ch of body.toLowerCase()) {
const digitValue = allowed.indexOf(ch);
if (digitValue < 0) {
throw new Error(`Invalid digit '${ch}' for base ${base}`);
}
acc = acc * radix + BigInt(digitValue);
}
return sign * acc;
}
/** Mask for a `width`-bit field: 2^width - 1. (Internal helper, not exported.) */
function mask(width) {
return (1n << BigInt(width)) - 1n;
}
/**
* Normalize any bigint to 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.
*/
export function normalize(n, width) {
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 '-'. (The third arg is a minimum digit count - the binary rendering
* of a width-bit value needs exactly `width` digits.)
*/
export function format(n, base, width) {
if (n < 0n) return '-' + format(-n, base, width);
const radix = BASE_RADIX[base];
let digits = n === 0n ? '0' : n.toString(radix);
while (digits.length < width) digits = '0' + digits;
return digits;
}
/**
* 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, a, b, width) {
const m = mask(width);
const x = normalize(a, width);
const y = normalize(b, width);
const w = BigInt(width);
switch (op) {
case 'and': return x & y;
case 'or': return x | y;
case 'xor': return x ^ y;
case 'not': return (~x) & m; // mask: BigInt ~ is unbounded
case 'shl':
// Left shift grows; for shift >= width every significant bit leaves the
// field, so the masked result is 0. Short-circuit to avoid materializing
// a (potentially huge) intermediate bigint.
return y >= w ? 0n : ((x << y) & m);
case 'shr':
// x is normalized non-negative, so >> is a logical (zero-filling) shift.
return y >= w ? 0n : (x >> y);
case 'rol':
case 'ror': {
const shift = y % w; // rotate amount wraps within width
if (shift === 0n) return x;
// A right-rotate by `shift` is a left-rotate by (width - shift).
const s = op === 'rol' ? shift : w - shift;
return ((x << s) | (x >> (w - s))) & m;
}
default:
throw new Error(`Unknown bitwise operation: ${op}`);
}
}
/** Fixed-width binary string of `width` bits (MSB first). */
export function toBits(n, width) {
return format(normalize(n, width), 'bin', width);
}
/** Indices of set bits (LSB = index 0), normalized to `width`. */
export function flags(n, width) {
const bits = normalize(n, width);
const out = [];
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 →