serfloat.cpp
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 }