diff --git a/src/Makefile.am b/src/Makefile.am index c5a7d0ad31..f3f58b41b7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -253,6 +253,7 @@ BITCOIN_CORE_H = \ util/moneystr.h \ util/rbf.h \ util/readwritefile.h \ + util/serfloat.h \ util/settings.h \ util/sock.h \ util/spanparsing.h \ @@ -595,6 +596,7 @@ libbitcoin_util_a_SOURCES = \ util/settings.cpp \ util/thread.cpp \ util/threadnames.cpp \ + util/serfloat.cpp \ util/spanparsing.cpp \ util/strencodings.cpp \ util/string.cpp \ diff --git a/src/util/serfloat.cpp b/src/util/serfloat.cpp new file mode 100644 index 0000000000..8edca924cd --- /dev/null +++ b/src/util/serfloat.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +double DecodeDouble(uint64_t v) noexcept { + static constexpr double NANVAL = std::numeric_limits::quiet_NaN(); + static constexpr double INFVAL = std::numeric_limits::infinity(); + double sign = 1.0; + if (v & 0x8000000000000000) { + sign = -1.0; + v ^= 0x8000000000000000; + } + // Zero + if (v == 0) return copysign(0.0, sign); + // Infinity + if (v == 0x7ff0000000000000) return copysign(INFVAL, sign); + // Other numbers + int exp = (v & 0x7FF0000000000000) >> 52; + uint64_t man = v & 0xFFFFFFFFFFFFF; + if (exp == 2047) { + // NaN + return NANVAL; + } else if (exp == 0) { + // Subnormal + return copysign(ldexp((double)man, -1074), sign); + } else { + // Normal + return copysign(ldexp((double)(man + 0x10000000000000), -1075 + exp), sign); + } +} + +uint64_t EncodeDouble(double f) noexcept { + int cls = std::fpclassify(f); + uint64_t sign = 0; + if (copysign(1.0, f) == -1.0) { + f = -f; + sign = 0x8000000000000000; + } + // Zero + if (cls == FP_ZERO) return sign; + // Infinity + if (cls == FP_INFINITE) return sign | 0x7ff0000000000000; + // NaN + if (cls == FP_NAN) return 0x7ff8000000000000; + // Other numbers + int exp; + uint64_t man = std::round(std::frexp(f, &exp) * 9007199254740992.0); + if (exp < -1021) { + // Too small to represent, encode 0 + if (exp < -1084) return sign; + // Subnormal numbers + return sign | (man >> (-1021 - exp)); + } else { + // Too big to represent, encode infinity + if (exp > 1024) return sign | 0x7ff0000000000000; + // Normal numbers + return sign | (((uint64_t)(1022 + exp)) << 52) | (man & 0xFFFFFFFFFFFFF); + } +} diff --git a/src/util/serfloat.h b/src/util/serfloat.h new file mode 100644 index 0000000000..4d912b0176 --- /dev/null +++ b/src/util/serfloat.h @@ -0,0 +1,16 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_SERFLOAT_H +#define BITCOIN_UTIL_SERFLOAT_H + +#include + +/* Encode a double using the IEEE 754 binary64 format. All NaNs are encoded as x86/ARM's + * positive quiet NaN with payload 0. */ +uint64_t EncodeDouble(double f) noexcept; +/* Reverse operation of DecodeDouble. DecodeDouble(EncodeDouble(f))==f unless isnan(f). */ +double DecodeDouble(uint64_t v) noexcept; + +#endif // BITCOIN_UTIL_SERFLOAT_H