Skip to content

64-bit BigInt bitwise ops produce wrong results (XOR, AND-mask, mul-wrap) #39

@proggeramlug

Description

@proggeramlug

Repro

const a: bigint = 0xCBF29CE484222325n;
const b = a ^ 5n;
console.log('a ^ 5n = ' + b.toString(16));
console.log('expected: cbf29ce484222320');

const mask = 0xFFFFFFFFFFFFFFFFn;
const c = (a * 0x100000001B3n) & mask;
console.log('(a * prime) & mask64 = ' + c.toString(16));
console.log('expected: bf9a804f79c4bcb7');

Expected

a ^ 5n = cbf29ce484222320
(a * prime) & mask64 = bf9a804f79c4bcb7

Actual (Perry 0.5.30)

a ^ 5n = -6
(a * prime) & mask64 = 0

Notes

XOR gives a small signed integer instead of flipping the low bits. AND-masking with 0xFFFFFFFFFFFFFFFFn collapses the value to zero. Multiplying and masking produces 0.

This makes any straightforward FNV-1a-64 / MurmurHash / xxhash-64 implementation unusable in user TS — they all rely on mul-then-mask-64 and xor with byte values. I ran into this while trying to get the three implementations (Rust / Zig / Perry) in the honest_bench benchmark to agree on a hash; switching every language to FNV-1a-32 with hand-rolled Math.imul-equivalent multiplication was the workaround.

Likely related to v0.5.24 ("bigint arithmetic + BigInt() coercion") having covered the numeric dispatch paths but not the bitwise ops.

Minimal repro file

See /tmp/perry_repros/C_bigint_bitops.ts (single-file, zero imports).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions