diff --git a/.github/workflows/jsroot-ci.yml b/.github/workflows/jsroot-ci.yml index 57081f38d..4b6a52cf4 100644 --- a/.github/workflows/jsroot-ci.yml +++ b/.github/workflows/jsroot-ci.yml @@ -67,6 +67,7 @@ jobs: node demo/node/file_proxy.js buffer ./hsimple.root node demo/node/rntuple_buffer_test.js node demo/node/rntuple_test.js demo/node/rntuple_test.root + node demo/node/rntuple_test32.js demo/node/rntuple_test32.root demo/node/rntuple_test32.json tests_ubuntu: runs-on: ubuntu-latest @@ -184,6 +185,8 @@ jobs: node demo/node/file_proxy.js buffer ./hsimple.root node demo/node/rntuple_buffer_test.js node demo/node/rntuple_test.js demo/node/rntuple_test.root + node demo/node/rntuple_test32.js demo/node/rntuple_test32.root demo/node/rntuple_test32.json + build-windows: runs-on: windows-latest @@ -235,3 +238,5 @@ jobs: node demo/node/file_proxy.js buffer ./hsimple.root node demo/node/rntuple_buffer_test.js node demo/node/rntuple_test.js demo/node/rntuple_test.root + node demo/node/rntuple_test32.js demo/node/rntuple_test32.root demo/node/rntuple_test32.json + diff --git a/demo/node/rntuple_test32.js b/demo/node/rntuple_test32.js new file mode 100644 index 000000000..8fecacdb9 --- /dev/null +++ b/demo/node/rntuple_test32.js @@ -0,0 +1,80 @@ +import { openFile, TSelector } from 'jsroot'; +import { rntupleProcess } from 'jsroot/rntuple'; +import { readFileSync } from 'fs'; + + +// convert float to hex representation based on IEEE-754 and C99 standard +function floatToHex(num) +{ + if (num === 0) + return (Object.is(num, -0) ? '-' : '') + '0x0p+0'; + + const buf = new ArrayBuffer(8), // 8 Byte == 64 Bit + view = new DataView(buf); + view.setFloat64(0, num, false); + + const bits = view.getBigUint64(0, false), // bitwise operations only work with integer + sign = Number(bits >> 63n), // sign on first bit + rawExp = Number((bits >> 52n) & 0x7ffn), // exponent on next 11 bits + fraction = bits & 0xfffffffffffffn; // fraction on next 52 bits + + let exp, leading, mantBits; + if (rawExp === 0) { + // check for really small numbers + if (fraction === 0n) + return (sign ? '-' : '') + '0x0p+0'; + exp = -1022; + leading = '0'; + mantBits = fraction; + } else { + exp = rawExp - 1023; + leading = '1'; + mantBits = fraction; + } + + const hex = mantBits.toString(16).padStart(13, '0').replace(/0+$/, ''), + frac = hex ? '.' + hex : '', + expStr = (exp >= 0 ? '+' : '-') + Math.abs(exp); + + return (sign ? '-' : '') + '0x' + leading + frac + 'p' + expStr; +} + +async function read_file(input_root = 'rntuple_test32.root', input_ref = 'rntuple_test32.json') +{ + const file = await openFile(input_root), rntuple = await file.readObject('ntpl'), + original = JSON.parse(readFileSync(input_ref)), + selector = new TSelector(), + fields = [ + 'FloatReal32Quant1', + 'FloatReal32Quant8', + 'FloatReal32Quant32', + 'DoubleReal32Quant1', + 'DoubleReal32Quant20', + 'DoubleReal32Quant32', + ]; + + for (const f of fields) + selector.addBranch(f); + + selector.Process = function(entryIndex) { + for (const field of fields) { + try { + const value = floatToHex(this.tgtobj[field]), expected = original[entryIndex][field]; + + if (value !== expected) { + console.error(`FAILURE: ${field} at entry ${entryIndex} expected ${expected},`+ + `got ${value}`); + } else + console.log(`OK: ${field} at entry ${entryIndex} = ${value}`); + } catch (err) { + console.error(`ERROR: Failed to read ${field} at entry ${entryIndex}: ${err.message}`); + } + } + }; + + await rntupleProcess(rntuple, selector); +} + +const [input_root, input_ref] = process.argv.slice(2); + +read_file(input_root, input_ref); diff --git a/demo/node/rntuple_test32.json b/demo/node/rntuple_test32.json new file mode 100644 index 000000000..bd8ba384c --- /dev/null +++ b/demo/node/rntuple_test32.json @@ -0,0 +1,42 @@ +[ + { + "FloatReal32Quant1": "-0x1p+0", + "FloatReal32Quant8": "-0x1.fdfdfdfdfdfep-2", + "FloatReal32Quant32": "0x1.0000000a1516p-2", + "DoubleReal32Quant1": "0x1p+0", + "DoubleReal32Quant20": "0x1p+0", + "DoubleReal32Quant32": "0x1.000000118p+1" + }, + { + "FloatReal32Quant1": "0x1p+0", + "FloatReal32Quant8": "0x1.0101010101p-8", + "FloatReal32Quant32": "0x1.921fbp-31", + "DoubleReal32Quant1": "0x1p+0", + "DoubleReal32Quant20": "0x1.00001p-20", + "DoubleReal32Quant32": "0x0p+0" + }, + { + "FloatReal32Quant1": "0x1p+0", + "FloatReal32Quant8": "0x1.0101010101p-8", + "FloatReal32Quant32": "0x1.921fbp-31", + "DoubleReal32Quant1": "0x1p+0", + "DoubleReal32Quant20": "0x1.00001p-20", + "DoubleReal32Quant32": "-0x1.2bfffffe0cp+5" + }, + { + "FloatReal32Quant1": "-0x1p+0", + "FloatReal32Quant8": "-0x1p+0", + "FloatReal32Quant32": "-0x1.921fb6p+1", + "DoubleReal32Quant1": "-0x1p+0", + "DoubleReal32Quant20": "-0x1p+0", + "DoubleReal32Quant32": "-0x1.9p+6" + }, + { + "FloatReal32Quant1": "0x1p+0", + "FloatReal32Quant8": "0x1p+0", + "FloatReal32Quant32": "0x1.921fb6p+1", + "DoubleReal32Quant1": "0x1p+0", + "DoubleReal32Quant20": "0x1p+0", + "DoubleReal32Quant32": "0x1.9p+4" + } +] diff --git a/demo/node/rntuple_test32.root b/demo/node/rntuple_test32.root new file mode 100644 index 000000000..c6db2b932 Binary files /dev/null and b/demo/node/rntuple_test32.root differ