{ "cells": [ { "cell_type": "markdown", "id": "a94f2bef", "metadata": {}, "source": [ "# ntpl018_low_precision_floats\n", "Example creating low-precision floating point fields in RNTuple\n", "\n", "RNTuple supports storing floating points on disk with less precision than their in-memory representation.\n", "Under the right circumstances, this can in save storage space while not significantly altering the results\n", "of an analysis.\n", "\n", "Storing low-precision floats is done by setting their column representation to one of the dedicated column types:\n", "- Real16 (half-precision IEEE754 fp),\n", "- Real32Trunc (single-precision IEEE754 with truncated mantissa, using from 10 to 31 bits in total) and\n", "- Real32Quant (floating point within a specified range, represented as an integer with N bits of precision in a\n", " linear quantized form).\n", "\n", "To use these column types in RNTuple, one creates a RField or RField and sets its desired column\n", "representation by calling, respectively:\n", "- RField::SetHalfPrecision() (for Real16)\n", "- RField::SetTruncated() (for Real32Trunc)\n", "- RField::SetQuantized() (for Real32Quant)\n", "\n", "Other than these, one can also setup the field to use the ROOT `Double32_t` type, either via\n", "RField::SetDouble32() or by directly creating one such field via RFieldBase::Create(\"f\", \"Double32_t\").\n", "\n", "\n", "\n", "\n", "**Author:** The ROOT Team \n", "This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:15 PM." ] }, { "cell_type": "code", "execution_count": 1, "id": "4fffce2c", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:38.927957Z", "iopub.status.busy": "2026-05-19T20:15:38.927846Z", "iopub.status.idle": "2026-05-19T20:15:38.932529Z", "shell.execute_reply": "2026-05-19T20:15:38.931982Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "static constexpr char const *kNTupleName = \"ntpl\";\n", "static constexpr char const *kNTupleFileName = \"ntpl018_low_precision_floats.root\";\n", "static constexpr int kNEvents = 50;\n", "\n", "struct Event {\n", " std::vector fPt;\n", " std::vector fE;\n", "};" ] }, { "cell_type": "markdown", "id": "26176182", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 2, "id": "c0325123", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:38.933921Z", "iopub.status.busy": "2026-05-19T20:15:38.933806Z", "iopub.status.idle": "2026-05-19T20:15:39.024791Z", "shell.execute_reply": "2026-05-19T20:15:39.024292Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "static void Write()\n", "{\n", " auto model = ROOT::RNTupleModel::Create();\n", "\n", " // Create 3 float fields: one backed by a Real16 column, one backed by a Real32Trunc column\n", " // and one backed by a Real32Quant column.\n", " // Since we need to call methods on the RField objects in order to make them into our specific column types,\n", " // we don't use MakeField but rather we explicitly create the RFields and then use AddField on the model.\n", " {\n", " auto fieldReal16 = std::make_unique>(\"myReal16\");\n", " fieldReal16->SetHalfPrecision(); // this is now a Real16-backed float field\n", " model->AddField(std::move(fieldReal16));\n", " }\n", " {\n", " auto fieldReal32Trunc = std::make_unique>(\"myReal32Trunc\");\n", " // Let's say we want 20 bits of precision. This means that this float's mantissa will be truncated to (20 - 9) =\n", " // 11 bits.\n", " fieldReal32Trunc->SetTruncated(20);\n", " model->AddField(std::move(fieldReal32Trunc));\n", " }\n", " {\n", " auto fieldReal32Quant = std::make_unique>(\"myReal32Quant\");\n", " // Declare that this field will never store values outside of the [-1, 1] range (this will be checked dynamically)\n", " // and that we want to dedicate 24 bits to this number on disk.\n", " fieldReal32Quant->SetQuantized(24, {-1., 1.});\n", " model->AddField(std::move(fieldReal32Quant));\n", " }\n", "\n", " // We can also change the column type of a struct/class subfield:\n", " {\n", " auto fieldEvents = std::make_unique>(\"myEvents\");\n", " // Note that we iterate over `*fieldEvents`, not over fieldEvents->GetMutableSubfields(), as the latter won't\n", " // recurse into fieldEvents's grandchildren. By iterating over the field itself we are sure to visit the entire\n", " // field hierarchy, including the fields we need to change.\n", " // The hierarchy of fieldEvents is like this:\n", " //\n", " // myEvents: RField\n", " // fPt: RField>\n", " // _0: RField <-- we need to change this\n", " // fE: RField\n", " // _0: RField <-- we need to change this\n", " //\n", " for (auto &field : *fieldEvents) {\n", " if (auto *fldDouble = dynamic_cast *>(&field)) {\n", " std::cout << \"Setting field \" << field.GetQualifiedFieldName() << \" to truncated.\\n\";\n", " fldDouble->SetTruncated(16);\n", " } else if (auto *fldFloat = dynamic_cast *>(&field)) {\n", " std::cout << \"Setting field \" << field.GetQualifiedFieldName() << \" to truncated.\\n\";\n", " fldFloat->SetTruncated(16);\n", " }\n", " }\n", " model->AddField(std::move(fieldEvents));\n", " }\n", "\n", " // Get the pointers to the fields we just added:\n", " const auto &entry = model->GetDefaultEntry();\n", " auto myReal16 = entry.GetPtr(\"myReal16\");\n", " auto myReal32Trunc = entry.GetPtr(\"myReal32Trunc\");\n", " auto myReal32Quant = entry.GetPtr(\"myReal32Quant\");\n", " auto myEvents = entry.GetPtr(\"myEvents\");\n", "\n", " auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), kNTupleName, kNTupleFileName);\n", "\n", " // fill our entries\n", " gRandom->SetSeed();\n", " for (int i = 0; i < kNEvents; i++) {\n", " *myReal16 = gRandom->Rndm();\n", " *myReal32Trunc = gRandom->Rndm();\n", " *myReal32Quant = gRandom->Rndm();\n", " myEvents->fPt.push_back(i);\n", " myEvents->fE.push_back(i);\n", " writer->Fill();\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "2bad0d38", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 3, "id": "6514a427", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:39.027133Z", "iopub.status.busy": "2026-05-19T20:15:39.026896Z", "iopub.status.idle": "2026-05-19T20:15:39.040316Z", "shell.execute_reply": "2026-05-19T20:15:39.039632Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "static void Read()\n", "{\n", " auto reader = ROOT::RNTupleReader::Open(kNTupleName, kNTupleFileName);\n", "\n", " // We can read back our fields as regular floats. We can also read them as double if we impose our own model when\n", " // creating the reader.\n", " const auto &entry = reader->GetModel().GetDefaultEntry();\n", " auto myReal16 = entry.GetPtr(\"myReal16\");\n", " auto myReal32Trunc = entry.GetPtr(\"myReal32Trunc\");\n", " auto myReal32Quant = entry.GetPtr(\"myReal32Quant\");\n", " auto myEvents = entry.GetPtr(\"myEvents\");\n", "\n", " for (auto idx : reader->GetEntryRange()) {\n", " reader->LoadEntry(idx);\n", "\n", " float eventsAvgPt = 0.f;\n", " for (float pt : myEvents->fPt)\n", " eventsAvgPt += pt;\n", " eventsAvgPt /= myEvents->fPt.size();\n", " double eventsAvgE = 0.f;\n", " for (double e : myEvents->fE)\n", " eventsAvgE += e;\n", " eventsAvgE /= myEvents->fE.size();\n", "\n", " std::cout << \"[\" << idx << \"] Real16: \" << *myReal16 << \", Real32Trunc: \" << *myReal32Trunc\n", " << \", Real32Quant: \" << *myReal32Quant << \", Events avg pt: \" << eventsAvgPt << \", E: \" << eventsAvgE\n", " << \"\\n\";\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 4, "id": "9b2856bd", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:39.041634Z", "iopub.status.busy": "2026-05-19T20:15:39.041502Z", "iopub.status.idle": "2026-05-19T20:15:40.032282Z", "shell.execute_reply": "2026-05-19T20:15:40.031592Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Setting field myEvents.fPt._0 to truncated.\n", "Setting field myEvents.fE._0 to truncated.\n", "[0] Real16: 0.0807495, Real32Trunc: 0.361938, Real32Quant: 0.381144, Events avg pt: 0, E: 0\n", "[1] Real16: 0.687988, Real32Trunc: 0.227478, Real32Quant: 0.460308, Events avg pt: 0.5, E: 0.5\n", "[2] Real16: 0.220459, Real32Trunc: 0.381226, Real32Quant: 0.55212, Events avg pt: 1, E: 1\n", "[3] Real16: 0.411377, Real32Trunc: 0.707764, Real32Quant: 0.376303, Events avg pt: 1.5, E: 1.5\n", "[4] Real16: 0.379395, Real32Trunc: 0.5625, Real32Quant: 0.216834, Events avg pt: 2, E: 2\n", "[5] Real16: 0.836426, Real32Trunc: 0.785156, Real32Quant: 0.423667, Events avg pt: 2.5, E: 2.5\n", "[6] Real16: 0.677246, Real32Trunc: 0.163696, Real32Quant: 0.699741, Events avg pt: 3, E: 3\n", "[7] Real16: 0.371094, Real32Trunc: 0.969238, Real32Quant: 0.156745, Events avg pt: 3.5, E: 3.5\n", "[8] Real16: 0.436035, Real32Trunc: 0.762695, Real32Quant: 0.142802, Events avg pt: 4, E: 4\n", "[9] Real16: 0.523926, Real32Trunc: 0.172119, Real32Quant: 0.198116, Events avg pt: 4.5, E: 4.5\n", "[10] Real16: 0.619141, Real32Trunc: 0.776367, Real32Quant: 0.89383, Events avg pt: 5, E: 5\n", "[11] Real16: 0.97998, Real32Trunc: 0.476562, Real32Quant: 0.841656, Events avg pt: 5.5, E: 5.5\n", "[12] Real16: 0.619629, Real32Trunc: 0.0752869, Real32Quant: 0.693117, Events avg pt: 6, E: 6\n", "[13] Real16: 0.178223, Real32Trunc: 0.234558, Real32Quant: 0.928468, Events avg pt: 6.5, E: 6.5\n", "[14] Real16: 0.48877, Real32Trunc: 0.619873, Real32Quant: 0.43516, Events avg pt: 7, E: 7\n", "[15] Real16: 0.540527, Real32Trunc: 0.54126, Real32Quant: 0.95247, Events avg pt: 7.5, E: 7.5\n", "[16] Real16: 0.790039, Real32Trunc: 0.267212, Real32Quant: 0.284743, Events avg pt: 8, E: 8\n", "[17] Real16: 0.609375, Real32Trunc: 0.772949, Real32Quant: 0.191841, Events avg pt: 8.5, E: 8.5\n", "[18] Real16: 0.436279, Real32Trunc: 0.419067, Real32Quant: 0.0510373, Events avg pt: 9, E: 9\n", "[19] Real16: 0.697266, Real32Trunc: 0.994873, Real32Quant: 0.739204, Events avg pt: 9.5, E: 9.5\n", "[20] Real16: 0.57959, Real32Trunc: 0.384277, Real32Quant: 0.354631, Events avg pt: 10, E: 10\n", "[21] Real16: 0.900879, Real32Trunc: 0.803955, Real32Quant: 0.56934, Events avg pt: 10.5, E: 10.5\n", "[22] Real16: 0.536621, Real32Trunc: 0.0113678, Real32Quant: 0.207262, Events avg pt: 11, E: 11\n", "[23] Real16: 0.465088, Real32Trunc: 0.0334625, Real32Quant: 0.315278, Events avg pt: 11.5, E: 11.5\n", "[24] Real16: 0.625977, Real32Trunc: 0.421631, Real32Quant: 0.0780235, Events avg pt: 12, E: 12\n", "[25] Real16: 0.368896, Real32Trunc: 0.20575, Real32Quant: 0.499459, Events avg pt: 12.5, E: 12.5\n", "[26] Real16: 0.0136719, Real32Trunc: 0.626709, Real32Quant: 0.826606, Events avg pt: 13, E: 13\n", "[27] Real16: 0.157349, Real32Trunc: 0.485229, Real32Quant: 0.580476, Events avg pt: 13.5, E: 13.5\n", "[28] Real16: 0.26001, Real32Trunc: 0.892578, Real32Quant: 0.99255, Events avg pt: 14, E: 14\n", "[29] Real16: 0.404785, Real32Trunc: 0.885254, Real32Quant: 0.776201, Events avg pt: 14.5, E: 14.5\n", "[30] Real16: 0.450195, Real32Trunc: 0.864014, Real32Quant: 0.668124, Events avg pt: 15, E: 15\n", "[31] Real16: 0.543945, Real32Trunc: 0.233093, Real32Quant: 0.176492, Events avg pt: 15.5, E: 15.5\n", "[32] Real16: 0.0394897, Real32Trunc: 0.944092, Real32Quant: 0.0121564, Events avg pt: 16, E: 16\n", "[33] Real16: 0.809082, Real32Trunc: 0.03479, Real32Quant: 0.745288, Events avg pt: 16.5, E: 16.5\n", "[34] Real16: 0.865723, Real32Trunc: 0.865723, Real32Quant: 0.0349109, Events avg pt: 17, E: 17\n", "[35] Real16: 0.262207, Real32Trunc: 0.586182, Real32Quant: 0.0269241, Events avg pt: 17.5, E: 17.5\n", "[36] Real16: 0.485596, Real32Trunc: 0.0554962, Real32Quant: 0.321083, Events avg pt: 18, E: 18\n", "[37] Real16: 0.697266, Real32Trunc: 0.586426, Real32Quant: 0.313495, Events avg pt: 18.5, E: 18.5\n", "[38] Real16: 0.0142899, Real32Trunc: 0.271606, Real32Quant: 0.296091, Events avg pt: 19, E: 19\n", "[39] Real16: 0.343994, Real32Trunc: 0.118561, Real32Quant: 0.0286675, Events avg pt: 19.5, E: 19.5\n", "[40] Real16: 0.0258484, Real32Trunc: 0.379761, Real32Quant: 0.243524, Events avg pt: 20, E: 20\n", "[41] Real16: 0.65332, Real32Trunc: 0.338867, Real32Quant: 0.532646, Events avg pt: 20.5, E: 20.5\n", "[42] Real16: 0.413818, Real32Trunc: 0.177124, Real32Quant: 0.208014, Events avg pt: 21, E: 21\n", "[43] Real16: 0.921387, Real32Trunc: 0.40625, Real32Quant: 0.908552, Events avg pt: 21.5, E: 21.5\n", "[44] Real16: 0.153931, Real32Trunc: 0.265991, Real32Quant: 0.0477834, Events avg pt: 22, E: 22\n", "[45] Real16: 0.635254, Real32Trunc: 0.795166, Real32Quant: 0.0125371, Events avg pt: 22.5, E: 22.5\n", "[46] Real16: 0.5, Real32Trunc: 0.838379, Real32Quant: 0.430773, Events avg pt: 23, E: 23\n", "[47] Real16: 0.861816, Real32Trunc: 0.582031, Real32Quant: 0.134407, Events avg pt: 23.5, E: 23.5\n", "[48] Real16: 0.620605, Real32Trunc: 0.136963, Real32Quant: 0.5748, Events avg pt: 24, E: 24\n", "[49] Real16: 0.981445, Real32Trunc: 0.674072, Real32Quant: 0.107088, Events avg pt: 24.5, E: 24.5\n" ] } ], "source": [ "Write();\n", "Read();" ] }, { "cell_type": "markdown", "id": "3c9abdf3", "metadata": {}, "source": [ "Draw all canvases " ] }, { "cell_type": "code", "execution_count": 5, "id": "f6e30dbc", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:15:40.033851Z", "iopub.status.busy": "2026-05-19T20:15:40.033730Z", "iopub.status.idle": "2026-05-19T20:15:40.238735Z", "shell.execute_reply": "2026-05-19T20:15:40.238065Z" } }, "outputs": [], "source": [ "gROOT->GetListOfCanvases()->Draw()" ] } ], "metadata": { "kernelspec": { "display_name": "ROOT C++", "language": "c++", "name": "root" }, "language_info": { "codemirror_mode": "text/x-c++src", "file_extension": ".C", "mimetype": " text/x-c++src", "name": "c++" } }, "nbformat": 4, "nbformat_minor": 5 }