ATLAS Offline Software
FloatPacker.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // $Id$
13 #include "CxxUtils/FloatPacker.h"
14 #include "CxxUtils/ones.h"
15 #include <limits>
16 #include <string>
17 #include <sstream>
18 #include <iomanip>
19 #include <stdexcept>
20 #ifndef __APPLE__
21 #include <ieee754.h> // ??? Is this standardized?
22 #else
23 //ieee754.h doesn't exist on MacOSX
24 union ieee754_double
25 {
26  long double d;
27  struct {
28  unsigned int negative:1;
29  unsigned int exponent:11;
30  /* Together these comprise the mantissa. */
31  unsigned int mantissa0:20;
32  unsigned int mantissa1:32;
33  } ieee;
34  struct {
35  unsigned int negative:1;
36  unsigned int exponent:11;
37  // cppcheck-suppress unusedStructMember
38  unsigned int quiet_nan:1;
39  /* Together these comprise the mantissa. */
40  unsigned int mantissa0:19;
41  unsigned int mantissa1:32;
42  } ieee_nan;
43 };
44 
45 #define IEEE754_DOUBLE_BIAS 0x3ff /* Added to exponent. */
46 
47 #endif
48 #include <stdint.h>
49 
50 
51 namespace {
52 
53 
54 
59 union double_or_int {
60  ieee754_double d;
61  uint32_t i[2];
62 };
63 
64 
65 const int ieee754_double_bias = 0x3ff;
66 const int ieee754_double_exponent_bits = 11;
67 const int ieee754_double_mantissa0_bits = 20;
68 const int ieee754_double_mantissa1_bits = 32;
69 
70 
72 typedef CxxUtils::FloatPacker::Packdest Packdest;
73 
75 const int packdest_bits = std::numeric_limits<Packdest>::digits;
76 
77 
78 // Handy constant: A Packdest with 1 in the high bit.
79 const Packdest high_packdest_bit = (1U << (packdest_bits - 1));
80 
81 const Packdest ieee754_double_exponent_mask =
82  (1U << ieee754_double_exponent_bits) - 1;
83 
84 
91 inline
92 int max_int (int nbits)
93 {
94  return ((1U << nbits) >> 1) - 1;
95 }
96 
97 
104 inline
105 int min_int (int nbits)
106 {
107  return static_cast<int>(~0U << nbits) >> 1;
108 }
109 
110 
119 void renormalize_denormal (int& exponent,
120  Packdest& mantissa)
121 {
122  if (mantissa == 0)
123  exponent -= packdest_bits; // Lost precision here.
124  else {
125  while ((mantissa & high_packdest_bit) == 0) {
126  --exponent;
127  mantissa <<= 1;
128  }
129  mantissa <<= 1;
130  }
131 }
132 
133 
147 void underflow_to_denormal (int min_exp,
148  int round_bits,
149  int& exponent,
150  Packdest& mantissa)
151 {
152  if (exponent <= min_exp) {
153  Packdest mantissa_in = mantissa;
154 
155  // Denormalize the mantissa.
156  mantissa = (mantissa >> 1) | high_packdest_bit;
157 
158  // Now shift it right.
159  int shift = min_exp - exponent;
160  if (shift < packdest_bits)
161  mantissa >>= shift;
162  else
163  mantissa = 0; // underflow to 0.
164 
165  // Flag it as denormal.
166  exponent = min_exp;
167 
168  // Handle rounding, if desired.
169  if (round_bits) {
170  //
171  // +- packdest_bits - round_bits - 1
172  // |- round_bits -|v
173  // mantissa: .................X....
174  // v- shift+1 -^
175  // mantissa_in: .....X................
176  // ^
177  // +- packdest_bits - round_bits + shift
178  //
179  int orig_pos = packdest_bits - round_bits + shift;
180  if (orig_pos < packdest_bits &&
181  ((static_cast<Packdest> (1) << orig_pos) & mantissa_in) != 0)
182  {
183  Packdest lsb = (static_cast<Packdest> (1) <<
184  (packdest_bits - round_bits));
185  Packdest lsbmask = ~ (lsb - 1);
186 
187  // ??? If we overflow here, it means we have to go back
188  // to a normalized representation. Just punt for now.
189  if ((mantissa & lsbmask) != lsbmask)
190  mantissa += lsb;
191  }
192  }
193  }
194 }
195 
196 
197 } // unnamed namespace
198 
199 
200 namespace CxxUtils {
201 
202 
214  int nmantissa,
215  double scale /*= 1*/,
216  bool is_signed /*= true*/,
217  bool round /*= false*/)
218  : m_nmantissa (nmantissa),
219  m_scale (scale),
220  m_is_signed (is_signed),
221  m_round (round)
222 {
223  // scale==0 means not to scale.
224  // Use that instead of 1 since it's faster to test for 0.
225  if (scale == 1)
226  scale = 0;
227 
228  if (scale == 0)
229  m_invscale = 0;
230  else
231  m_invscale = 1. / m_scale;
232 
233  // Set up other cached values.
235  if (m_is_signed)
236  --m_npack;
237 
238  m_npack_ones = ones<Packdest> (m_npack);
239 
240  // Sign bit mask.
241  if (m_is_signed)
242  m_signmask = 1U << (nbits - 1);
243  else
244  m_signmask = 0;
245 
246  // Number of exponent bits.
247  m_nexp = nbits - m_nmantissa;
248  m_nexp_ones = ones<Packdest> (m_nexp);
249 
250  // Minimum exponent value.
251  m_min_exp = min_int (m_nexp);
252 
253  // Maximum exponent value.
254  m_max_exp = max_int (m_nexp);
255 
256  if (m_npack < 1 || m_npack > nbits)
257  throw std::runtime_error ("Bad number of mantissa bits.");
258 }
259 
260 
271 FloatPacker::pack (double src, std::string* err /*= nullptr*/) const
272 {
273  double_or_int d;
274  d.d.d = src;
275 
276  // Fast-path for zero. (Purely an optimization.)
277  // Note: can't use a double compare here. On some architectures (eg, MIPS)
278  // a denormal will compare equal to zero.
279  if (d.i[0] == 0 && d.i[1] == 0)
280  return 0;
281 
282  // Check for NaN and infinity.
283  if (d.d.ieee.exponent == ieee754_double_exponent_mask) {
284  if (err) {
285  std::ostringstream os;
286  os << "Bad float number: " << src << " (" << std::setbase(16) << d.i[0]
287  << " " << d.i[1] << ")";
288  *err = os.str();
289  }
290  d.d.d = 0;
291  }
292 
293  if (m_invscale)
294  d.d.d *= m_invscale;
295 
296  bool was_negative = false;
297  if (d.d.ieee.negative != 0) {
298  if (m_is_signed) {
299  was_negative = true;
300  d.d.d = -d.d.d;
301  }
302  else {
303  // Don't complain on -0.
304  if (d.d.d < 0 && err) {
305  std::ostringstream os;
306  os << "Float overflow during packing: " << src;
307  *err = os.str();
308  }
309  d.d.d = 0;
310  }
311  }
312 
313  // Check for zero again.
314  // (Also need to preserve the sign; the scale division may
315  // have underflowed.)
316  if (d.i[0] == 0 && d.i[1] == 0) {
317  if (was_negative)
318  return m_signmask;
319  else
320  return 0;
321  }
322 
323  // Get packdest_bits bits of mantissa.
324 
325  Packdest mantissa =
326  (d.d.ieee.mantissa0 << (packdest_bits -
327  ieee754_double_mantissa0_bits)) |
328  (d.d.ieee.mantissa1 >>
329  (ieee754_double_mantissa1_bits -
330  (packdest_bits - ieee754_double_mantissa0_bits)));
331 
332  // Get the unbiased exponent.
333  int exponent =
334  static_cast<int> (d.d.ieee.exponent) - ieee754_double_bias;
335 
336  // Do rounding, if requested.
337  if (m_round) {
338  Packdest lsbmask = (1 << (packdest_bits - m_npack));
339  int roundbit;
340  Packdest roundmask;
341  if (lsbmask > 1) {
342  roundbit = (mantissa & (lsbmask >> 1));
343  roundmask = ~ static_cast<Packdest> (roundbit - 1);
344  }
345  else {
346  roundbit = (d.d.ieee.mantissa1 &
347  ((1 << ((ieee754_double_mantissa1_bits -
348  (packdest_bits -
349  ieee754_double_mantissa0_bits)) - 1))));
350  roundmask = ~ static_cast<Packdest> (0);
351  }
352 
353  if (roundbit != 0) {
354  // Handle the case where it would overflow.
355  if ((mantissa & roundmask) == roundmask) {
356  mantissa >>= 1;
357  mantissa |= roundmask;
358  exponent += 1;
359  }
360 
361  mantissa += lsbmask;
362  }
363  }
364 
365  // If the number is too large, bitch, and reset to the largest number.
366  if (exponent > m_max_exp) {
367  if (err) {
368  std::ostringstream os;
369  os << "Float overflow during packing: " << src;
370  *err = os.str();
371  }
372  exponent = m_max_exp;
373  mantissa = static_cast<Packdest> (~0);
374  }
375 
376  // Handle denormals. (We've already handled the zero case.)
377  if (exponent == - ieee754_double_bias)
378  renormalize_denormal (exponent, mantissa);
379 
380  // If the number is too small, denormalize, or underflow to 0.
381  underflow_to_denormal (m_min_exp, m_round ? m_npack: 0, exponent, mantissa);
382 
383  // Pack in the mantissa bits.
384  Packdest dest = mantissa >> (packdest_bits - m_npack);
385 
386  // The exponent, if desired.
387  if (m_nexp > 0)
388  dest |= ((exponent - m_min_exp) << m_npack);
389 
390  // And the optional sign bit.
391  if (was_negative)
392  dest |= m_signmask;
393 
394  return dest;
395 }
396 
397 
405 double FloatPacker::unpack (Packdest val, std::string* err /*= nullptr*/) const
406 {
407  // Fast-path for 0.
408  if (val == 0)
409  return 0;
410 
411  // Break apart the packed value.
412  bool was_negative = false;
413  if ((val & m_signmask) != 0)
414  was_negative = true;
415 
416  double d;
417 
418  // Fast path for fixed-point representations.
419  if (m_nexp == 0) {
420  Packdest mantissa = (val & m_npack_ones);
421  d = mantissa / ((double)m_npack_ones + 1);
422  if (was_negative)
423  d *= -1;
424  }
425  else {
426  // Get the mantissa.
427  Packdest mantissa = (val & m_npack_ones) << (packdest_bits - m_npack);
428 
429  // General case.
430  // Get the exponent.
431  int exponent = ((val >> m_npack) & m_nexp_ones);
432  exponent += m_min_exp; // unbias.
433 
434  ieee754_double dd;
435 
436  // Handle denormals.
437  if (exponent == m_min_exp) {
438  // Maybe it was -0?
439  if (mantissa == 0) {
440  dd.d = 0;
441  if (was_negative)
442  dd.ieee.negative = 1;
443  return dd.d;
444  }
445 
446  renormalize_denormal (exponent, mantissa);
447  }
448 
449  // Complain about overflow.
450  if (exponent >= max_int (ieee754_double_exponent_bits)) {
451  if (err) {
452  std::ostringstream os;
453  os << "Overflow while unpacking float; exponent: " << exponent;
454  *err = os.str();
455  }
456  exponent = max_int (ieee754_double_exponent_bits) + 1;
457  mantissa = 0; // Infinity.
458  }
459 
460  // Underflow into denormal.
461  underflow_to_denormal ( - ieee754_double_bias, 0,
462  exponent, mantissa);
463 
464  // Pack into a double.
465  dd.ieee.negative = was_negative ? 1 : 0;
466  dd.ieee.exponent = exponent + ieee754_double_bias;
467  dd.ieee.mantissa0 =
468  (mantissa >> (packdest_bits - ieee754_double_mantissa0_bits));
469  dd.ieee.mantissa1 =
470  (mantissa << (ieee754_double_mantissa0_bits -
471  (packdest_bits - ieee754_double_mantissa1_bits)));
472  d = dd.d;
473  }
474 
475  // Set the result.
476  if (m_scale)
477  d *= m_scale;
478  return d;
479 }
480 
481 
482 } // namespace CxxUtils
CxxUtils::FloatPacker::FloatPacker
FloatPacker(int nbits, int nmantissa, double scale=1, bool is_signed=true, bool round=false)
Constructor.
Definition: FloatPacker.cxx:213
xAOD::uint32_t
setEventNumber uint32_t
Definition: EventInfo_v1.cxx:127
WriteCellNoiseToCool.src
src
Definition: WriteCellNoiseToCool.py:513
hist_file_dump.d
d
Definition: hist_file_dump.py:137
MuonGM::round
float round(const float toRound, const unsigned int decimals)
Definition: Mdt.cxx:27
CxxUtils::FloatPacker::m_is_signed
bool m_is_signed
Should we use a sign bit?
Definition: FloatPacker.h:114
perfmonmt-printer.dest
dest
Definition: perfmonmt-printer.py:189
yodamerge_tmp.scale
scale
Definition: yodamerge_tmp.py:138
CxxUtils::FloatPacker::m_npack_ones
Packdest m_npack_ones
Mask with that many low bits set.
Definition: FloatPacker.h:123
CxxUtils::FloatPacker::m_signmask
Packdest m_signmask
Mask containing the sign bit (or 0 if there's no sign bit).
Definition: FloatPacker.h:126
CxxUtils::FloatPacker::m_min_exp
int m_min_exp
Minimum exponent value.
Definition: FloatPacker.h:135
dqt_zlumi_pandas.err
err
Definition: dqt_zlumi_pandas.py:193
lumiFormat.i
int i
Definition: lumiFormat.py:92
CxxUtils::FloatPacker::m_nexp
int m_nexp
Number of exponent bits.
Definition: FloatPacker.h:129
CxxUtils
Definition: aligned_vector.h:29
CxxUtils::FloatPacker::m_round
bool m_round
Should we round instead of truncating?
Definition: FloatPacker.h:117
CxxUtils::FloatPacker::Packdest
uint32_t Packdest
Type into which we pack.
Definition: FloatPacker.h:61
xAOD::double
double
Definition: CompositeParticle_v1.cxx:159
CxxUtils::FloatPacker::pack
Packdest pack(double src, std::string *err=nullptr) const
Pack a value.
Definition: FloatPacker.cxx:271
ones.h
Construct a bit mask.
ReadFromCoolCompare.os
os
Definition: ReadFromCoolCompare.py:231
library_scraper.dd
list dd
Definition: library_scraper.py:46
CxxUtils::FloatPacker::m_max_exp
int m_max_exp
Maximum exponent value.
Definition: FloatPacker.h:138
CxxUtils::FloatPacker::m_nmantissa
int m_nmantissa
Number of bits in the mantissa + sign bit.
Definition: FloatPacker.h:105
CxxUtils::FloatPacker::m_nexp_ones
Packdest m_nexp_ones
Mask with that many low bits set.
Definition: FloatPacker.h:132
CxxUtils::FloatPacker::unpack
double unpack(Packdest val, std::string *err=nullptr) const
Unpack the value VAL.
Definition: FloatPacker.cxx:405
Pythia8_RapidityOrderMPI.val
val
Definition: Pythia8_RapidityOrderMPI.py:14
FloatPacker.h
Pack/unpack floating-point data from/to a given number of bits.
CxxUtils::FloatPacker::m_invscale
double m_invscale
Inverse of scale.
Definition: FloatPacker.h:111
CxxUtils::FloatPacker::m_npack
int m_npack
Number of bits in mantissa (exclusive of any sign bit).
Definition: FloatPacker.h:120
CxxUtils::FloatPacker::m_scale
double m_scale
Scale factor for stored numbers.
Definition: FloatPacker.h:108