Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooNaNPacker.h
Go to the documentation of this file.
1/*
2 * RooNaNPacker.h
3 *
4 * Created on: 28.04.2020
5 * Author: shageboeck
6 */
7
8#ifndef ROOFIT_ROOFITCORE_INC_ROONANPACKER_H_
9#define ROOFIT_ROOFITCORE_INC_ROONANPACKER_H_
10
11#include <TError.h>
12
13#include <limits>
14#include <cstdint>
15#include <cmath>
16#include <cassert>
17#include <numeric>
18#include <cstring>
19
20/// Little struct that can pack a float into the unused bits of the mantissa of a
21/// NaN double. This can be used to transport information about violation
22/// of function definition ranges, negative PDFs or other computation
23/// problems in RooFit.
24/// To separate NaNs that contain packed floats from regular NaNs, a tag is
25/// written into the upper bits of the mantissa. If this tag is found, a payload
26/// can be recovered. Otherwise, the NaN is assumed to originate from other sources
27/// than a RooFit class that wants to signal to the minimiser.
29 double _payload;
30
31 // In double-valued NaNs, on can abuse the lowest 51 bit for payloads.
32 // We use this to pack a float into the lowest 32 bits, which leaves
33 // 19-bit to include a magic tag to tell NaNs with payload from ordinary
34 // NaNs:
35 static constexpr uint64_t magicTagMask = 0x3ffff00000000;
36 static constexpr uint64_t magicTag = 0x321ab00000000;
37
38 constexpr RooNaNPacker() :
39 _payload(0.) { }
40
41 /// Create NaN with a packed floating point number.
42 explicit RooNaNPacker(float value) :
43 _payload(packFloatIntoNaN(value)) { }
44
45 /// Pack float into mantissa of NaN.
46 void setPayload(float payload) {
47 _payload = packFloatIntoNaN(payload);
48
49 if (!std::isnan(_payload)) {
50 // Big-endian machine or other. Just return NaN.
51 warn();
52 _payload = std::numeric_limits<double>::quiet_NaN();
53 }
54 }
55
56 /// Accumulate a packed float from another NaN into `this`.
57 void accumulate(double val) {
58 *this += unpackNaN(val);
59 }
60
61 /// Unpack floats from NaNs, and sum the packed values.
62 template<class It_t>
63 static double accumulatePayloads(It_t begin, It_t end) {
64 double sum = std::accumulate(begin, end, 0.f, [](float acc, double val) {
65 return acc += unpackNaN(val);
66 });
67
68 return packFloatIntoNaN(sum);
69 }
70
71 /// Add to the packed float.
73 setPayload(getPayload() + val);
74 return *this;
75 }
76
77 /// Multiply the packed float.
79 setPayload(getPayload() * val);
80 return *this;
81 }
82
83 /// Retrieve packed float. Returns zero if number is not NaN
84 /// or if float wasn't packed by this class.
85 float getPayload() const {
87 }
88
89 /// Retrieve a NaN with the current float payload packed into the mantissa.
90 double getNaNWithPayload() const {
91 return _payload;
92 }
93
94 /// Test if this struct has a float packed into its mantissa.
95 bool isNaNWithPayload() const {
97 }
98
99 /// Test if `val` has a float packed into its mantissa.
100 static bool isNaNWithPayload(double val) {
101 uint64_t tmp;
102 std::memcpy(&tmp, &val, sizeof(uint64_t));
103 return std::isnan(val) && (tmp & magicTagMask) == magicTag;
104 }
105
106 /// Pack float into mantissa of a NaN. Adds a tag to the
107 /// upper bits of the mantissa, so a "normal" NaN can be
108 /// differentiated from a NaN with a payload.
109 static double packFloatIntoNaN(float payload) {
110 double result = std::numeric_limits<double>::quiet_NaN();
111 uint64_t tmp;
112 std::memcpy(&tmp, &result, sizeof(uint64_t));
113 tmp |= magicTag;
114 std::memcpy(&tmp, &payload, sizeof(float));
115 std::memcpy(&result, &tmp, sizeof(uint64_t));
116 return result;
117 }
118
119 /// If `val` is NaN and a this NaN has been tagged as containing
120 /// a payload, unpack the float from the mantissa.
121 /// Return 0 otherwise.
122 static float unpackNaN(double val) {
123 float tmp;
124 std::memcpy(&tmp, &val, sizeof(float));
125 return isNaNWithPayload(val) ? tmp : 0.;
126 }
127
128 /// Warn that packing only works on little-endian machines.
129 static void warn() {
130 static bool haveWarned = false;
131 if (!haveWarned)
132 Warning("RooNaNPacker", "Fast recovery from undefined function values only implemented for little-endian machines."
133 " If necessary, request an extension of functionality on https://root.cern");
134 haveWarned = true;
135 }
136};
137
138
139#endif /* ROOFIT_ROOFITCORE_INC_ROONANPACKER_H_ */
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:231
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.
static constexpr uint64_t magicTag
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
float getPayload() const
Retrieve packed float.
static bool isNaNWithPayload(double val)
Test if val has a float packed into its mantissa.
static double packFloatIntoNaN(float payload)
Pack float into mantissa of a NaN.
static constexpr uint64_t magicTagMask
double _payload
void setPayload(float payload)
Pack float into mantissa of NaN.
constexpr RooNaNPacker()
RooNaNPacker(float value)
Create NaN with a packed floating point number.
double getNaNWithPayload() const
Retrieve a NaN with the current float payload packed into the mantissa.
RooNaNPacker & operator*=(float val)
Multiply the packed float.
static double accumulatePayloads(It_t begin, It_t end)
Unpack floats from NaNs, and sum the packed values.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
void accumulate(double val)
Accumulate a packed float from another NaN into this.
RooNaNPacker & operator+=(float val)
Add to the packed float.
static void warn()
Warn that packing only works on little-endian machines.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345