serfloat.cpp raw

   1  // Copyright (c) 2021-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <util/serfloat.h>
   6  
   7  #include <cmath>
   8  #include <limits>
   9  
  10  double DecodeDouble(uint64_t v) noexcept {
  11      static constexpr double NANVAL = std::numeric_limits<double>::quiet_NaN();
  12      static constexpr double INFVAL = std::numeric_limits<double>::infinity();
  13      double sign = 1.0;
  14      if (v & 0x8000000000000000) {
  15          sign = -1.0;
  16          v ^= 0x8000000000000000;
  17      }
  18      // Zero
  19      if (v == 0) return copysign(0.0, sign);
  20      // Infinity
  21      if (v == 0x7ff0000000000000) return copysign(INFVAL, sign);
  22      // Other numbers
  23      int exp = (v & 0x7FF0000000000000) >> 52;
  24      uint64_t man = v & 0xFFFFFFFFFFFFF;
  25      if (exp == 2047) {
  26          // NaN
  27          return NANVAL;
  28      } else if (exp == 0) {
  29          // Subnormal
  30          return copysign(ldexp((double)man, -1074), sign);
  31      } else {
  32          // Normal
  33          return copysign(ldexp((double)(man + 0x10000000000000), -1075 + exp), sign);
  34      }
  35  }
  36  
  37  uint64_t EncodeDouble(double f) noexcept {
  38      int cls = std::fpclassify(f);
  39      uint64_t sign = 0;
  40      if (copysign(1.0, f) == -1.0) {
  41          f = -f;
  42          sign = 0x8000000000000000;
  43      }
  44      // Zero
  45      if (cls == FP_ZERO) return sign;
  46      // Infinity
  47      if (cls == FP_INFINITE) return sign | 0x7ff0000000000000;
  48      // NaN
  49      if (cls == FP_NAN) return 0x7ff8000000000000;
  50      // Other numbers
  51      int exp;
  52      uint64_t man = std::round(std::frexp(f, &exp) * 9007199254740992.0);
  53      if (exp < -1021) {
  54          // Too small to represent, encode 0
  55          if (exp < -1084) return sign;
  56          // Subnormal numbers
  57          return sign | (man >> (-1021 - exp));
  58      } else {
  59          // Too big to represent, encode infinity
  60          if (exp > 1024) return sign | 0x7ff0000000000000;
  61          // Normal numbers
  62          return sign | (((uint64_t)(1022 + exp)) << 52) | (man & 0xFFFFFFFFFFFFF);
  63      }
  64  }
  65