Skip to content

Fix reading of negative 64-bit integers - #422

Merged
linev merged 1 commit into
root-project:devfrom
Prafyl:fix-ntoi8-negative
Oct 9, 2026
Merged

linev merged 1 commit into
root-project:devfrom
Prafyl:fix-ntoi8-negative

Conversation

@Prafyl

@Prafyl Prafyl commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

This Pull request:

fixes negative Long64_t values being read wrong. for some negative numbers jsroot shows a different value than root, e.g. -4294967296 shows up as 0.

repro tree:

void make_int64() {
   TFile f("int64.root", "RECREATE");
   TTree t("t", "t");
   Long64_t v;
   t.Branch("v", &v, "v/L");
   for (Long64_t x : {-1LL, -4294967296LL, -4294967297LL, -9007199254740992LL, (Long64_t)0x8000000000000000ULL}) {
      v = x;
      t.Fill();
   }
   t.Write();
}

t->Scan("v", "", "col=22lld") in root vs dumping v in jsroot:

root                    jsroot (before)          jsroot (after)
-1                      -1                       -1
-4294967296             0                        -4294967296
-4294967297             -4294967297              -4294967297
-9007199254740992       -9007194959773696        -9007199254740992
-9223372036854775808    -9223372032559808512n    -9223372036854775808n

Changes:

in TBuffer.ntoi8() the negative branch uses ~high and ~low directly. ~ gives back a signed int32, so when the low word is below 0x80000000 ~low is negative and the result is off by +2^32. the bigint branch had the same problem. i convert both back to unsigned with >>> 0 before combining them. values where the low word has the top bit set (like -1 and -4294967297) were already right, which is probably why nobody noticed.

i checked ntoi8() against DataView.getBigInt64() for 0, ±1, -2, -2^31, -2^32, -2^32-1, -5e9, -(2^53-1), -2^53, INT64_MIN, 2^32, 2^53-1 and INT64_MAX. before 4 of them were wrong, now all 14 match. positive values and ntou8() arent touched.

Fixes:

jsroot now shows the same values as root for every Long64_t.

~ returns a signed int32, so negative Long64_t values whose low word is
below 0x80000000 were read off by +2^32 (e.g. -2^32 came back as 0).
Convert ~high and ~low back to unsigned with >>> 0 before combining.
@linev linev self-assigned this Oct 9, 2026
@linev linev added the bug fix label Oct 9, 2026
@linev

linev commented Oct 9, 2026

Copy link
Copy Markdown
Member

Thanks for the fix.

Can you also provide test which one can add to CI.
One can add such small ROOT file directly to repository and provide test like:

https://github.com/root-project/jsroot/blob/master/demo/node/rntuple_test.js

And include this test to:

https://github.com/root-project/jsroot/blob/master/.github/workflows/jsroot-ci.yml

It would be grate if you can implement this.

@linev
linev merged commit cb604ba into root-project:dev Oct 9, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants