Bitwise Calculator — Java 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 Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// =============================================================================
// bitwise.java — CosmoDev polyglot showcase port of the `bitwise` tool
// -----------------------------------------------------------------------------
// Language : Java (Java 17, standard library only)
// Source: ported from src/lib/bitwise.ts (the canonical, live TypeScript
// lib); mirrors src/tool-sources/bitwise/{python.py,rust.rs}
// License : display source — part of CosmoDev's polyglot tool pages
// (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
// Python ports and the other language ports.
// -----------------------------------------------------------------------------
// Pure, deterministic bitwise calculator. Zero deps. 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 integer is always the unsigned
// bit-pattern of the width-bit result.
//
// Java note: the TS source uses arbitrary-precision `bigint`. Java has no
// unsigned or 128-bit integer type, so every normalized value lives in a
// `long` holding the UNSIGNED bit pattern (values in [2^63, 2^64) appear
// "negative" — only the bits are meaningful). All arithmetic that can exceed
// 2^63 goes through Long's unsigned helpers (divideUnsigned /
// remainderUnsigned / compareUnsigned) or wrapping two's-complement
// arithmetic, which is exact for the masked result. Only literals beyond
// 2^64-1 diverge from the TS lib (thrown as a clean out-of-range), far
// outside any realistic bitwise-calculator input.
// =============================================================================
import java.util.ArrayList;
import java.util.List;
public final class Bitwise {
private Bitwise() {
}
/** Numeric radix used for parsing and formatting. */
public enum Base {
BIN("bin", 2, "01"),
OCT("oct", 8, "01234567"),
DEC("dec", 10, "0123456789"),
HEX("hex", 16, "0123456789abcdef");
final String lower;
final int radix;
final String digits;
Base(String lower, int radix, String digits) {
this.lower = lower;
this.radix = radix;
this.digits = digits;
}
}
/** Bit-width of the virtual register (8/16/32/64). */
public static final int[] WIDTHS = {8, 16, 32, 64};
/**
* Supported bitwise operation. {@code NOT} is unary on {@code a}; the rest
* are binary, with {@code b} as the shift/rotate count for the
* shift/rotate ops.
*/
public enum Op {AND, OR, XOR, NOT, SHL, SHR, ROL, ROR}
/**
* Parses a numeric string in {@code base} into a signed {@code long}.
* Strips 0x/0b/0o prefixes and an optional leading sign. Throws
* {@link IllegalArgumentException} on empty input, any out-of-base digit,
* or a literal whose magnitude exceeds 2^64-1. The raw signed value is
* returned (no width normalization); callers fold it into a field via
* {@link #normalize} / {@link #bitwise}.
*
* <p>Magnitudes accumulate as unsigned bit patterns in a {@code long} and
* negate by wrapping subtraction, so the full unsigned range (e.g. hex
* 0xFFFFFFFFFFFFFFFF) round-trips exactly — a wrap of 2^64 is invisible
* once the value is width-normalized.
*/
public static long parse(String value, Base base) {
String trimmed = value.strip();
if (trimmed.isEmpty() || trimmed.equals("-")) {
throw new IllegalArgumentException("Empty " + base.lower + " value");
}
// Peel off an optional leading '-' so negative literals parse correctly.
boolean negative = trimmed.charAt(0) == '-';
String body = negative ? trimmed.substring(1) : trimmed;
// Strip each base prefix in turn (0x, then 0b, then 0o) from a lowered
// copy — mirrors the reference's chained leading-prefix removal.
// Matching against the lowercased copy keeps "0xFF" valid.
String lowered = body.toLowerCase(java.util.Locale.ROOT);
for (String prefix : new String[]{"0x", "0b", "0o"}) {
if (lowered.startsWith(prefix)) {
lowered = lowered.substring(prefix.length());
}
}
if (lowered.isEmpty()) {
throw new IllegalArgumentException("Empty " + base.lower + " value");
}
// Horner's method over the digit alphabet, with a checked accumulate
// (unsigned view): an over-wide literal reports a clean error instead
// of wrapping.
String allowed = base.digits;
long radix = base.radix;
long acc = 0L;
for (int i = 0; i < lowered.length(); i++) {
char ch = lowered.charAt(i);
int digit = allowed.indexOf(ch);
if (digit < 0) {
throw new IllegalArgumentException("Invalid digit '" + ch + "' for base " + base.lower);
}
// acc must satisfy acc * radix + digit <= 2^64 - 1, i.e.
// acc <= floor((2^64 - 1 - digit) / radix), computed unsigned.
long limit = Long.divideUnsigned(-1L - digit, radix); // -1L - digit wraps to 2^64-1-digit
if (Long.compareUnsigned(acc, limit) > 0) {
throw new IllegalArgumentException("Value out of range for base " + base.lower);
}
// Wrapping multiply/add: exact mod 2^64, and the check above
// guarantees no wrap actually occurs.
acc = acc * radix + digit;
}
// Wrapping negate is exact mod 2^64 (two's complement).
return negative ? 0L - acc : acc;
}
/**
* Mask for a {@code width}-bit field: 2^width - 1. Width 64 is spelled
* directly because 1 << 64 would wrap in a long.
*/
public static long mask(int width) {
return width == 64 ? -1L : (1L << width) - 1L;
}
/**
* Normalizes any signed value to its unsigned width-bit two's-complement
* value, i.e. into the half-open range [0, 2^width) — e.g. -1 at width 8
* yields 255. The result is the unsigned bit pattern in a long.
*
* <p>The reference computes {@code ((n % m) + m) % m} with m = 2^width on
* arbitrary-precision ints. For width < 64 that is exactly
* {@link Math#floorMod}; for width 64 a long already IS n mod 2^64
* (two's-complement truncation), so the pattern passes through as-is.
*/
public static long normalize(long n, int width) {
return width == 64 ? n : Math.floorMod(n, 1L << width);
}
/**
* Renders {@code n} in {@code base}, zero-padded to at least
* {@code minDigits} digits. Negatives carry a leading '-' and format
* their magnitude (via {@link Long#toUnsignedString(long, int)}, which
* reads the magnitude off the bit pattern — exact even past 2^63).
* {@code minDigits} corresponds to the {@code width} parameter of the TS
* reference (a width-bit binary value needs exactly {@code width} digits).
*/
public static String format(long n, Base base, int minDigits) {
if (n < 0) {
// Wrapping negate yields the true magnitude as a bit pattern.
long mag = 0L - n;
return "-" + formatUnsigned(mag, base, minDigits);
}
return formatUnsigned(n, base, minDigits);
}
// Long.toUnsignedString produces the lowercase digits the TS
// `bigint.toString(radix)` call produces; manual padding adds the zeros.
private static String formatUnsigned(long v, Base base, int minDigits) {
String digits = Long.toUnsignedString(v, base.radix); // "0" renders naturally for zero
if (digits.length() < minDigits) {
digits = "0".repeat(minDigits - digits.length()) + digits;
}
return digits;
}
/**
* Applies a width-bit operation. {@code a} is the (unary) operand for
* NOT; {@code 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 (an unsigned bit pattern)
* is masked to {@code width} bits.
*
* <p>For shl/shr we short-circuit when the shift count meets or exceeds
* the width: every significant bit is shifted out, so the masked result
* is zero (this also keeps every shift amount below 64 — Java masks
* long-shift counts to 6 bits, so an unguarded shift would silently wrap).
* Left shifts keep only the low 64 bits of the true bigint shift, which is
* lossless because the mask never keeps bits at or above bit 64.
*/
public static long bitwise(Op op, long a, long b, int width) {
long m = mask(width);
long x = normalize(a, width);
long 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:
if (Long.compareUnsigned(y, width) >= 0) {
return 0L;
}
return (x << y) & m; // y < 64 here, so the shift count is exact
case SHR:
// x is normalized non-negative as a pattern → logical
// (zero-filling) shift, which is Java's >>> on the pattern.
if (Long.compareUnsigned(y, width) >= 0) {
return 0L;
}
return x >>> y;
case ROL:
case ROR: {
long shift = Long.remainderUnsigned(y, width); // rotate amount wraps within width
if (shift == 0) {
return x;
}
// A right-rotate by `shift` is a left-rotate by (width - shift).
long s = op == Op.ROL ? shift : width - shift;
return ((x << s) | (x >>> (width - s))) & m; // s, width - s < 64 here
}
default:
throw new IllegalArgumentException("Unknown bitwise operation: " + op);
}
}
/**
* Fixed-width binary string of {@code width} bits (MSB first). Formats
* the unsigned normalized pattern directly — width-64 patterns can exceed
* Long.MAX_VALUE and must not round-trip through the signed path.
*/
public static String toBits(long n, int width) {
return formatUnsigned(normalize(n, width), Base.BIN, width);
}
/**
* Indices of set bits (LSB = index 0), normalized to {@code width}, in
* ascending order. Stops at the highest set bit (v != 0 is pattern
* non-zero).
*/
public static List<Integer> flags(long n, int width) {
long v = normalize(n, width);
List<Integer> out = new ArrayList<>();
int i = 0;
while (v != 0L) {
if ((v & 1L) != 0L) {
out.add(i);
}
v >>>= 1;
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 →