Found while benchmarking BigInt division (#9141); reproduces on pristine main 653e886699, unrelated to that change.
Repro
const c = [2n ** 64n];
console.log(-c[0]);
|
result |
| node |
-18446744073709551616 |
| perry |
-18446744073709552000 |
The perry value is the f64 rounding of 2^64 — the BigInt is being converted to a double and float-negated. Silent wrong value, no throw.
Characterization
- Reading
c[0] alone is correct (prints the exact BigInt).
-v where v is a plain local holding the same BigInt is correct.
- Only unary minus applied directly to an array element mis-lowers.
- An explicit
bigint[] annotation does not help.
So the element read produces the right value and the unary-minus lowering picks the numeric path regardless of the operand's runtime type.
Why it matters
BigInt arrays are the normal representation for big-number tables (hash constants, crypto limbs, byte→BigInt lookup tables like TypeBox's). A silently-rounded negation in that code produces wrong results with no diagnostic.
Repro file: secret-tests/scratchpad/probe_q2.ts.
Found while benchmarking BigInt division (#9141); reproduces on pristine main
653e886699, unrelated to that change.Repro
-18446744073709551616-18446744073709552000The perry value is the f64 rounding of 2^64 — the BigInt is being converted to a double and float-negated. Silent wrong value, no throw.
Characterization
c[0]alone is correct (prints the exact BigInt).-vwherevis a plain local holding the same BigInt is correct.bigint[]annotation does not help.So the element read produces the right value and the unary-minus lowering picks the numeric path regardless of the operand's runtime type.
Why it matters
BigInt arrays are the normal representation for big-number tables (hash constants, crypto limbs, byte→BigInt lookup tables like TypeBox's). A silently-rounded negation in that code produces wrong results with no diagnostic.
Repro file:
secret-tests/scratchpad/probe_q2.ts.