Skip to content

Commit 2be4cd9

Browse files
committed
Add platform-independent float encoder/decoder
1 parent e40224d commit 2be4cd9

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ BITCOIN_CORE_H = \
253253
util/moneystr.h \
254254
util/rbf.h \
255255
util/readwritefile.h \
256+
util/serfloat.h \
256257
util/settings.h \
257258
util/sock.h \
258259
util/spanparsing.h \
@@ -595,6 +596,7 @@ libbitcoin_util_a_SOURCES = \
595596
util/settings.cpp \
596597
util/thread.cpp \
597598
util/threadnames.cpp \
599+
util/serfloat.cpp \
598600
util/spanparsing.cpp \
599601
util/strencodings.cpp \
600602
util/string.cpp \

src/util/serfloat.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2021 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+
}

src/util/serfloat.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2021 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+
#ifndef BITCOIN_UTIL_SERFLOAT_H
6+
#define BITCOIN_UTIL_SERFLOAT_H
7+
8+
#include <stdint.h>
9+
10+
/* Encode a double using the IEEE 754 binary64 format. All NaNs are encoded as x86/ARM's
11+
* positive quiet NaN with payload 0. */
12+
uint64_t EncodeDouble(double f) noexcept;
13+
/* Reverse operation of DecodeDouble. DecodeDouble(EncodeDouble(f))==f unless isnan(f). */
14+
double DecodeDouble(uint64_t v) noexcept;
15+
16+
#endif // BITCOIN_UTIL_SERFLOAT_H

0 commit comments

Comments
 (0)