Skip to content

Commit

Permalink
Merge bitcoin#29192: Weaken serfloat tests
Browse files Browse the repository at this point in the history
6e873df serfloat: improve/simplify tests (Pieter Wuille)
b45f1f5 serfloat: do not test encode(bits)=bits anymore (Pieter Wuille)

Pull request description:

  Closes bitcoin#28941.

  Our current tests for serfloat verify two distinct properties:
  1. Whether they roundtrip `double`->`uint64_t`->`double` (excluding NaN values) on all systems.
  2. Whether on systems with a typical floating point unit that encoding matches the hardware representation, as before v22.0, we would dump the hardware representation directly to disk and we wanted to retain compatibility with that.

  bitcoin#28941 seems to show that the second property doesn't always hold, but just for "subnormal" numbers (below $2^{-1021}$). Since we don't care about encoding these numbers, we could exclude such subnormal numbers from the hardware-identical representation test, but this PR goes further and just drops the second property entirely, as I don't think we care about edge-case compatibility with pre-v22.0 code for fee_estimates.dat (the only place it is used).

ACKs for top commit:
  glozow:
    ACK 6e873df
  fanquake:
    ACK 6e873df - It's not as much of a priority, but I think we could still backport this.

Tree-SHA512: e18ceee0753a7ee7e999fdfa10b014dc5bb67b6ef79522a0f8c76b889adcfa785772fc26ed7559bcb5a09a9938e243bb54eedd9549bc59080a2c8090155e2267
  • Loading branch information
fanquake committed Mar 19, 2024
2 parents 8e95a9c + 6e873df commit 479ecc0
Showing 1 changed file with 69 additions and 47 deletions.
116 changes: 69 additions & 47 deletions src/test/serfloat_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ uint64_t TestDouble(double f) {
} // namespace

BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
// Test specific values against their expected encoding.
BOOST_CHECK_EQUAL(TestDouble(0.0), 0U);
BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000U);
Expand All @@ -46,55 +47,76 @@ BOOST_AUTO_TEST_CASE(double_serfloat_tests) {
BOOST_CHECK_EQUAL(TestDouble(2.0), 0x4000000000000000ULL);
BOOST_CHECK_EQUAL(TestDouble(4.0), 0x4010000000000000ULL);
BOOST_CHECK_EQUAL(TestDouble(785.066650390625), 0x4088888880000000ULL);
BOOST_CHECK_EQUAL(TestDouble(3.7243058682384174), 0x400dcb60e0031440);
BOOST_CHECK_EQUAL(TestDouble(91.64070592566159), 0x4056e901536d447a);
BOOST_CHECK_EQUAL(TestDouble(-98.63087668642575), 0xc058a860489c007a);
BOOST_CHECK_EQUAL(TestDouble(4.908737756962054), 0x4013a28c268b2b70);
BOOST_CHECK_EQUAL(TestDouble(77.9247330021754), 0x40537b2ed3547804);
BOOST_CHECK_EQUAL(TestDouble(40.24732825357566), 0x40441fa873c43dfc);
BOOST_CHECK_EQUAL(TestDouble(71.39395607929222), 0x4051d936938f27b6);
BOOST_CHECK_EQUAL(TestDouble(58.80100710817612), 0x404d668766a2bd70);
BOOST_CHECK_EQUAL(TestDouble(-30.10665786964975), 0xc03e1b4dee1e01b8);
BOOST_CHECK_EQUAL(TestDouble(60.15231509068704), 0x404e137f0f969814);
BOOST_CHECK_EQUAL(TestDouble(-48.15848711335961), 0xc04814494e445bc6);
BOOST_CHECK_EQUAL(TestDouble(26.68450101125353), 0x403aaf3b755169b0);
BOOST_CHECK_EQUAL(TestDouble(-65.72071986604303), 0xc0506e2046378ede);
BOOST_CHECK_EQUAL(TestDouble(17.95575825512381), 0x4031f4ac92b0a388);
BOOST_CHECK_EQUAL(TestDouble(-35.27171863226279), 0xc041a2c7ad17a42a);
BOOST_CHECK_EQUAL(TestDouble(-8.58810329425124), 0xc0212d1bdffef538);
BOOST_CHECK_EQUAL(TestDouble(88.51393044338977), 0x405620e43c83b1c8);
BOOST_CHECK_EQUAL(TestDouble(48.07224932612732), 0x4048093f77466ffc);
BOOST_CHECK_EQUAL(TestDouble(9.867348871395659e+117), 0x586f4daeb2459b9f);
BOOST_CHECK_EQUAL(TestDouble(-1.5166424385129721e+206), 0xeabe3bbc484bd458);
BOOST_CHECK_EQUAL(TestDouble(-8.585156555624594e-275), 0x8707c76eee012429);
BOOST_CHECK_EQUAL(TestDouble(2.2794371091628822e+113), 0x5777b2184458f4ee);
BOOST_CHECK_EQUAL(TestDouble(-1.1290476594131867e+163), 0xe1c91893d3488bb0);
BOOST_CHECK_EQUAL(TestDouble(9.143848423979275e-246), 0x0d0ff76e5f2620a3);
BOOST_CHECK_EQUAL(TestDouble(-2.8366718125941117e+81), 0xd0d7ec7e754b394a);
BOOST_CHECK_EQUAL(TestDouble(-1.2754409481684012e+229), 0xef80d32f8ec55342);
BOOST_CHECK_EQUAL(TestDouble(6.000577060053642e-186), 0x197a1be7c8209b6a);
BOOST_CHECK_EQUAL(TestDouble(2.0839423284378986e-302), 0x014c94f8689cb0a5);
BOOST_CHECK_EQUAL(TestDouble(-1.422140051483753e+259), 0xf5bd99271d04bb35);
BOOST_CHECK_EQUAL(TestDouble(-1.0593973991188853e+46), 0xc97db0cdb72d1046);
BOOST_CHECK_EQUAL(TestDouble(2.62945125875249e+190), 0x67779b36366c993b);
BOOST_CHECK_EQUAL(TestDouble(-2.920377657275094e+115), 0xd7e7b7b45908e23b);
BOOST_CHECK_EQUAL(TestDouble(9.790289014855851e-118), 0x27a3c031cc428bcc);
BOOST_CHECK_EQUAL(TestDouble(-4.629317182034961e-114), 0xa866ccf0b753705a);
BOOST_CHECK_EQUAL(TestDouble(-1.7674605603846528e+279), 0xf9e8ed383ffc3e25);
BOOST_CHECK_EQUAL(TestDouble(2.5308171727712605e+120), 0x58ef5cd55f0ec997);
BOOST_CHECK_EQUAL(TestDouble(-1.05034156412799e+54), 0xcb25eea1b9350fa0);

// Roundtrip test on IEC559-compatible systems
if (std::numeric_limits<double>::is_iec559) {
BOOST_CHECK_EQUAL(sizeof(double), 8U);
BOOST_CHECK_EQUAL(sizeof(uint64_t), 8U);
// Test extreme values
TestDouble(std::numeric_limits<double>::min());
TestDouble(-std::numeric_limits<double>::min());
TestDouble(std::numeric_limits<double>::max());
TestDouble(-std::numeric_limits<double>::max());
TestDouble(std::numeric_limits<double>::lowest());
TestDouble(-std::numeric_limits<double>::lowest());
TestDouble(std::numeric_limits<double>::quiet_NaN());
TestDouble(-std::numeric_limits<double>::quiet_NaN());
TestDouble(std::numeric_limits<double>::signaling_NaN());
TestDouble(-std::numeric_limits<double>::signaling_NaN());
TestDouble(std::numeric_limits<double>::denorm_min());
TestDouble(-std::numeric_limits<double>::denorm_min());
// Test exact encoding: on currently supported platforms, EncodeDouble
// should produce exactly the same as the in-memory representation for non-NaN.
for (int j = 0; j < 1000; ++j) {
// Iterate over 9 specific bits exhaustively; the others are chosen randomly.
// These specific bits are the sign bit, and the 2 top and bottom bits of
// exponent and mantissa in the IEEE754 binary64 format.
for (int x = 0; x < 512; ++x) {
uint64_t v = InsecureRandBits(64);
v &= ~(uint64_t{1} << 0);
if (x & 1) v |= (uint64_t{1} << 0);
v &= ~(uint64_t{1} << 1);
if (x & 2) v |= (uint64_t{1} << 1);
v &= ~(uint64_t{1} << 50);
if (x & 4) v |= (uint64_t{1} << 50);
v &= ~(uint64_t{1} << 51);
if (x & 8) v |= (uint64_t{1} << 51);
v &= ~(uint64_t{1} << 52);
if (x & 16) v |= (uint64_t{1} << 52);
v &= ~(uint64_t{1} << 53);
if (x & 32) v |= (uint64_t{1} << 53);
v &= ~(uint64_t{1} << 61);
if (x & 64) v |= (uint64_t{1} << 61);
v &= ~(uint64_t{1} << 62);
if (x & 128) v |= (uint64_t{1} << 62);
v &= ~(uint64_t{1} << 63);
if (x & 256) v |= (uint64_t{1} << 63);
double f;
memcpy(&f, &v, 8);
uint64_t v2 = TestDouble(f);
if (!std::isnan(f)) BOOST_CHECK_EQUAL(v, v2);
// Test extreme values
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::min()), 0x10000000000000);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::min()), 0x8010000000000000);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::max()), 0x7fefffffffffffff);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::max()), 0xffefffffffffffff);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::lowest()), 0xffefffffffffffff);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::lowest()), 0x7fefffffffffffff);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::denorm_min()), 0x1);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::denorm_min()), 0x8000000000000001);
// Note that all NaNs are encoded the same way.
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::quiet_NaN()), 0x7ff8000000000000);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::quiet_NaN()), 0x7ff8000000000000);
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::signaling_NaN()), 0x7ff8000000000000);
BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits<double>::signaling_NaN()), 0x7ff8000000000000);

// Construct doubles to test from the encoding.
static_assert(sizeof(double) == 8);
static_assert(sizeof(uint64_t) == 8);
for (int j = 0; j < 1000; ++j) {
// Iterate over 9 specific bits exhaustively; the others are chosen randomly.
// These specific bits are the sign bit, and the 2 top and bottom bits of
// exponent and mantissa in the IEEE754 binary64 format.
for (int x = 0; x < 512; ++x) {
uint64_t v = InsecureRandBits(64);
int x_pos = 0;
for (int v_pos : {0, 1, 50, 51, 52, 53, 61, 62, 63}) {
v &= ~(uint64_t{1} << v_pos);
if ((x >> (x_pos++)) & 1) v |= (uint64_t{1} << v_pos);
}
double f;
memcpy(&f, &v, 8);
TestDouble(f);
}
}
}
Expand Down

0 comments on commit 479ecc0

Please sign in to comment.