Logo ROOT  
Reference Guide
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.
28 struct RooNaNPacker {
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.
72  RooNaNPacker& operator+=(float val) {
73  setPayload(getPayload() + val);
74  return *this;
75  }
76 
77  /// Multiply the packed float.
78  RooNaNPacker& operator*=(float val) {
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 {
96  return isNaNWithPayload(_payload);
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_ */
RooNaNPacker::getNaNWithPayload
double getNaNWithPayload() const
Retrieve a NaN with the current float payload packed into the mantissa.
Definition: RooNaNPacker.h:90
RooNaNPacker::magicTagMask
static constexpr uint64_t magicTagMask
Definition: RooNaNPacker.h:35
Warning
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition: TError.cxx:231
RooNaNPacker::_payload
double _payload
Definition: RooNaNPacker.h:29
RooNaNPacker::packFloatIntoNaN
static double packFloatIntoNaN(float payload)
Pack float into mantissa of a NaN.
Definition: RooNaNPacker.h:109
RooNaNPacker::warn
static void warn()
Warn that packing only works on little-endian machines.
Definition: RooNaNPacker.h:129
RooNaNPacker
Little struct that can pack a float into the unused bits of the mantissa of a NaN double.
Definition: RooNaNPacker.h:28
sum
static uint64_t sum(uint64_t i)
Definition: Factory.cxx:2345
RooNaNPacker::accumulate
void accumulate(double val)
Accumulate a packed float from another NaN into this.
Definition: RooNaNPacker.h:57
RooNaNPacker::isNaNWithPayload
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
Definition: RooNaNPacker.h:95
RooNaNPacker::accumulatePayloads
static double accumulatePayloads(It_t begin, It_t end)
Unpack floats from NaNs, and sum the packed values.
Definition: RooNaNPacker.h:63
RooNaNPacker::isNaNWithPayload
static bool isNaNWithPayload(double val)
Test if val has a float packed into its mantissa.
Definition: RooNaNPacker.h:100
RooNaNPacker::getPayload
float getPayload() const
Retrieve packed float.
Definition: RooNaNPacker.h:85
RooNaNPacker::operator+=
RooNaNPacker & operator+=(float val)
Add to the packed float.
Definition: RooNaNPacker.h:72
isnan
int isnan(double)
RooNaNPacker::RooNaNPacker
constexpr RooNaNPacker()
Definition: RooNaNPacker.h:38
RooNaNPacker::magicTag
static constexpr uint64_t magicTag
Definition: RooNaNPacker.h:36
RooNaNPacker::unpackNaN
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...
Definition: RooNaNPacker.h:122
RooNaNPacker::RooNaNPacker
RooNaNPacker(float value)
Create NaN with a packed floating point number.
Definition: RooNaNPacker.h:42
RooNaNPacker::operator*=
RooNaNPacker & operator*=(float val)
Multiply the packed float.
Definition: RooNaNPacker.h:78
RooNaNPacker::setPayload
void setPayload(float payload)
Pack float into mantissa of NaN.
Definition: RooNaNPacker.h:46
TError.h