From 618ed27779163d1fa83edbbc0514433c1e29d8b3 Mon Sep 17 00:00:00 2001
From: Bruce Toll <4109762+tollb@users.noreply.github.com>
Date: Thu, 6 Jan 2022 10:16:29 -0500
Subject: [PATCH 1/3] Enhance round-trip testing of floats in Wt::Dbo

Add a set of tests to check if floats and doubles round-trip
successfully with Wt::Dbo.

The test values (and associated comments) were adapted from
Postgres source files as noted in comments.

NOTE: Subnormals that do not match exactly are currently reported as a
warning, rather than a failure, if they are close in value.
---
 test/dbo/DboTest.C | 540 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 540 insertions(+)

diff --git a/test/dbo/DboTest.C b/test/dbo/DboTest.C
index 2ac1091f..0f2bafa3 100644
--- a/test/dbo/DboTest.C
+++ b/test/dbo/DboTest.C
@@ -4,6 +4,7 @@
  * See the LICENSE file for terms of use.
  */
 #include <boost/test/unit_test.hpp>
+#include <boost/predef.h>
 
 #include <Wt/Dbo/Dbo.h>
 #include <Wt/Dbo/FixedSqlConnectionPool.h>
@@ -3384,4 +3385,543 @@ BOOST_AUTO_TEST_CASE( dbo_test45 )
   }
 }
 
+BOOST_AUTO_TEST_CASE( dbo_test46 )
+{
+// To be conservative, only run on x86 architecture due to byte-order dependencies
+// in converting hex_values to floating point
+#ifdef BOOST_ARCH_X86
+  // Check for issues, including loss of precision, with float values that should round-trip
+  // Test data adapted from:
+  // https://raw.githubusercontent.com/postgres/postgres/REL_12_9/src/test/regress/sql/float4.sql
+  // See license: https://raw.githubusercontent.com/postgres/postgres/REL_12_9/COPYRIGHT
+  DboFixture f;
+
+  float quiet_nan = std::nanf("");
+  static const uint32_t quiet_nan_hex = *(reinterpret_cast<uint32_t*>(&quiet_nan));
+
+  float positive_infinity = std::numeric_limits<float>::infinity();
+  static const uint32_t positive_infinity_hex = *(reinterpret_cast<uint32_t*>(&positive_infinity));
+
+  float negative_infinity = -std::numeric_limits<float>::infinity();
+  static const uint32_t negative_infinity_hex = *(reinterpret_cast<uint32_t*>(&negative_infinity));
+
+  static const uint32_t hex_values[] {
+    // some special values
+    quiet_nan_hex,
+    positive_infinity_hex,
+    negative_infinity_hex,
+
+    // small subnormals
+    0x00000001,
+    0x00000002, 0x00000003,
+    0x00000010, 0x00000011, 0x00000100, 0x00000101,
+    0x00004000, 0x00004001, 0x00080000, 0x00080001,
+
+    // stress values
+    0x0053c4f4,  // 7693e-42
+    0x006c85c4,  // 996622e-44
+    0x0041ca76,  // 60419369e-46
+    0x004b7678,  // 6930161142e-48
+
+    // taken from upstream testsuite
+    0x00000007,
+    0x00424fe2,
+
+    // borderline between subnormal and normal
+    0x007ffff0, 0x007ffff1, 0x007ffffe, 0x007fffff,
+
+    // additional tests
+    0x00000000,
+
+    // smallest normal values
+    0x00800000, 0x00800001, 0x00800004, 0x00800005,
+    0x00800006,
+
+    // small normal values chosen for short vs. long output
+    0x008002f1, 0x008002f2, 0x008002f3,
+    0x00800e17, 0x00800e18, 0x00800e19,
+
+    // assorted values (random mantissae)
+    0x01000001, 0x01102843, 0x01a52c98,
+    0x0219c229, 0x02e4464d, 0x037343c1, 0x03a91b36,
+    0x047ada65, 0x0496fe87, 0x0550844f, 0x05999da3,
+    0x060ea5e2, 0x06e63c45, 0x07f1e548, 0x0fc5282b,
+    0x1f850283, 0x2874a9d6,
+
+    // values around 5e-08
+    0x3356bf94, 0x3356bf95, 0x3356bf96,
+
+    // around 1e-07
+    0x33d6bf94, 0x33d6bf95, 0x33d6bf96,
+
+    // around 3e-07 .. 1e-04
+    0x34a10faf, 0x34a10fb0, 0x34a10fb1,
+    0x350637bc, 0x350637bd, 0x350637be,
+    0x35719786, 0x35719787, 0x35719788,
+    0x358637bc, 0x358637bd, 0x358637be,
+    0x36a7c5ab, 0x36a7c5ac, 0x36a7c5ad,
+    0x3727c5ab, 0x3727c5ac, 0x3727c5ad,
+
+    // format crossover at 1e-04
+    0x38d1b714, 0x38d1b715, 0x38d1b716,
+    0x38d1b717, 0x38d1b718, 0x38d1b719,
+    0x38d1b71a, 0x38d1b71b, 0x38d1b71c,
+    0x38d1b71d,
+
+    0x38dffffe, 0x38dfffff, 0x38e00000,
+    0x38efffff, 0x38f00000, 0x38f00001,
+    0x3a83126e, 0x3a83126f, 0x3a831270,
+    0x3c23d709, 0x3c23d70a, 0x3c23d70b,
+    0x3dcccccc, 0x3dcccccd, 0x3dccccce,
+
+    // chosen to need 9 digits for 3dcccd70
+    0x3dcccd6f, 0x3dcccd70, 0x3dcccd71,
+
+    0x3effffff, 0x3f000000, 0x3f000001,
+    0x3f333332, 0x3f333333, 0x3f333334,
+
+    // approach 1.0 with increasing numbers of 9s
+    0x3f666665, 0x3f666666, 0x3f666667,
+    0x3f7d70a3, 0x3f7d70a4, 0x3f7d70a5,
+    0x3f7fbe76, 0x3f7fbe77, 0x3f7fbe78,
+    0x3f7ff971, 0x3f7ff972, 0x3f7ff973,
+    0x3f7fff57, 0x3f7fff58, 0x3f7fff59,
+    0x3f7fffee, 0x3f7fffef,
+
+    // values very close to 1
+    0x3f7ffff0, 0x3f7ffff1, 0x3f7ffff2,
+    0x3f7ffff3, 0x3f7ffff4, 0x3f7ffff5,
+    0x3f7ffff6, 0x3f7ffff7, 0x3f7ffff8,
+    0x3f7ffff9, 0x3f7ffffa, 0x3f7ffffb,
+    0x3f7ffffc, 0x3f7ffffd, 0x3f7ffffe,
+    0x3f7fffff,
+    0x3f800000,
+    0x3f800001, 0x3f800002, 0x3f800003,
+    0x3f800004, 0x3f800005, 0x3f800006,
+    0x3f800007, 0x3f800008, 0x3f800009,
+
+    // values 1 to 1.1
+    0x3f80000f, 0x3f800010, 0x3f800011,
+    0x3f800012, 0x3f800013, 0x3f800014,
+    0x3f800017, 0x3f800018, 0x3f800019,
+    0x3f80001a, 0x3f80001b, 0x3f80001c,
+    0x3f800029, 0x3f80002a, 0x3f80002b,
+    0x3f800053, 0x3f800054, 0x3f800055,
+    0x3f800346, 0x3f800347, 0x3f800348,
+    0x3f8020c4, 0x3f8020c5, 0x3f8020c6,
+    0x3f8147ad, 0x3f8147ae, 0x3f8147af,
+    0x3f8ccccc, 0x3f8ccccd, 0x3f8cccce,
+
+    0x3fc90fdb, // pi/2
+    0x402df854, // e
+    0x40490fdb, // pi
+
+    0x409fffff, 0x40a00000, 0x40a00001,
+    0x40afffff, 0x40b00000, 0x40b00001,
+    0x411fffff, 0x41200000, 0x41200001,
+    0x42c7ffff, 0x42c80000, 0x42c80001,
+    0x4479ffff, 0x447a0000, 0x447a0001,
+    0x461c3fff, 0x461c4000, 0x461c4001,
+    0x47c34fff, 0x47c35000, 0x47c35001,
+    0x497423ff, 0x49742400, 0x49742401,
+    0x4b18967f, 0x4b189680, 0x4b189681,
+    0x4cbebc1f, 0x4cbebc20, 0x4cbebc21,
+    0x4e6e6b27, 0x4e6e6b28, 0x4e6e6b29,
+    0x501502f8, 0x501502f9, 0x501502fa,
+    0x51ba43b6, 0x51ba43b7, 0x51ba43b8,
+
+    // stress values
+    0x1f6c1e4a,  // 5e-20
+    0x59be6cea,  // 67e14
+    0x5d5ab6c4,  // 985e15
+    0x2cc4a9bd,  // 55895e-16
+    0x15ae43fd,  // 7038531e-32
+    0x2cf757ca,  // 702990899e-20
+    0x665ba998,  // 25933168707e13
+    0x743c3324,  // 596428896559e20
+
+    // exercise fixed-point memmoves
+    0x47f1205a,
+    0x4640e6ae,
+    0x449a5225,
+    0x42f6e9d5,
+    0x414587dd,
+    0x3f9e064b,
+
+    // these cases come from the upstream's testsuite
+    // BoundaryRoundEven
+    0x4c000004,
+    0x50061c46,
+    0x510006a8,
+
+    // ExactValueRoundEven
+    0x48951f84,
+    0x45fd1840,
+
+    // LotsOfTrailingZeros
+    0x39800000,
+    0x3b200000,
+    0x3b900000,
+    0x3bd00000,
+
+    // Regression
+    0x63800000,
+    0x4b000000,
+    0x4b800000,
+    0x4c000001,
+    0x4c800b0d,
+    0x00d24584,
+    0x00d90b88,
+    0x45803f34,
+    0x4f9f24f7,
+    0x3a8722c3,
+    0x5c800041,
+    0x15ae43fd,
+    0x5d4cccfb,
+    0x4c800001,
+    0x57800ed8,
+    0x5f000000,
+    0x700000f0,
+    0x5f23e9ac,
+    0x5e9502f9,
+    0x5e8012b1,
+    0x3c000028,
+    0x60cde861,
+    0x03aa2a50,
+    0x43480000,
+    0x4c000000,
+
+    // LooksLikePow5
+    0x5D1502F9,
+    0x5D9502F9,
+    0x5E1502F9,
+
+    // OutputLength
+    0x3f99999a,
+    0x3f9d70a4,
+    0x3f9df3b6,
+    0x3f9e0419,
+    0x3f9e0610,
+    0x3f9e064b,
+    0x3f9e0651,
+    0x03d20cfe
+  };
+
+  for (int i = 0; i < sizeof hex_values / sizeof hex_values[0]; ++i) {
+    auto hex_value = hex_values[i];
+    float fl = *(reinterpret_cast<float*>(&hex_value));
+    char buf[100];
+    snprintf(buf, sizeof buf, "%.9g", fl);
+    BOOST_TEST_MESSAGE ("hex_value: " << std::hex << hex_value << std::dec << ", value: " << buf );
+
+    float fl_from_database = 0.0f;
+    std::string exception_msg;
+
+    try {
+      {
+        dbo::Transaction t(*f.session_);
+        auto a = f.session_->addNew<A>();
+        a.modify()->i = i;
+        a.modify()->f = fl;
+      }
+
+#ifdef POSTGRES
+      {
+        dbo::Transaction t(*f.session_);
+        std::string fl_from_database_s = f.session_->query<std::string>(
+          "SELECT \"f\" FROM " SCHEMA "\"table_a\" WHERE \"i\"=?"
+        ).bind(i).resultValue();
+        BOOST_TEST_MESSAGE ("PG text value: " << fl_from_database_s );
+      }
+#endif
+
+      {
+        dbo::Transaction t(*f.session_);
+        auto a = f.session_->find<A>().where("\"i\" = ?").bind(i).resultValue();
+        fl_from_database = a->f;
+      }
+    } catch (std::exception& e) {
+      exception_msg = std::string("unexpected exception: ") + e.what();
+    }
+
+    BOOST_TEST(exception_msg.empty(), exception_msg);
+    if (exception_msg.empty()) {
+      if (std::isnan(fl)) {
+        BOOST_CHECK(std::isnan(fl_from_database));
+      }
+      else if (std::isinf(fl)) {
+        BOOST_CHECK(std::isinf(fl_from_database));
+        BOOST_CHECK_EQUAL(fl_from_database, fl);
+      }
+      else if (std::fpclassify(fl) == FP_SUBNORMAL) {
+        // kludge to improve warning (highlighting that it is a subnormal)
+        float subnormal_fl = fl;
+        BOOST_CHECK_CLOSE(fl_from_database, subnormal_fl, 1e-5);
+        // only warn if subnormals do not match, since implementations/support may vary
+        BOOST_WARN_EQUAL(fl_from_database, subnormal_fl);
+      }
+      else {
+        BOOST_CHECK_CLOSE(fl_from_database, fl, 1e-5);
+        BOOST_CHECK_EQUAL(fl_from_database, fl);
+      }
+    }
+  }
+#endif
+}
+
+BOOST_AUTO_TEST_CASE( dbo_test47 )
+{
+// To be conservative, only run on x86 architecture due to byte-order dependencies
+// in converting hex_values to floating point
+#ifdef BOOST_ARCH_X86
+  // Check for issues, including loss of precision, with double values that should round-trip
+  // Test data adapted from:
+  // https://raw.githubusercontent.com/postgres/postgres/REL_12_9/src/test/regress/sql/float8.sql
+  // See license: https://raw.githubusercontent.com/postgres/postgres/REL_12_9/COPYRIGHT
+  DboFixture f;
+
+  double quiet_nan = std::nan("");
+  static const uint64_t quiet_nan_hex = *(reinterpret_cast<uint64_t*>(&quiet_nan));
+
+  double positive_infinity = std::numeric_limits<double>::infinity();
+  static const uint64_t positive_infinity_hex = *(reinterpret_cast<uint64_t*>(&positive_infinity));
+
+  double negative_infinity = -std::numeric_limits<double>::infinity();
+  static const uint64_t negative_infinity_hex = *(reinterpret_cast<uint64_t*>(&negative_infinity));
+
+  static const uint64_t hex_values[] {
+    // some special values
+    quiet_nan_hex,
+    positive_infinity_hex,
+    negative_infinity_hex,
+
+    // small subnormals
+    0x0000000000000001,
+    0x0000000000000002, 0x0000000000000003,
+    0x0000000000001000, 0x0000000100000000,
+    0x0000010000000000, 0x0000010100000000,
+    0x0000400000000000, 0x0000400100000000,
+    0x0000800000000000, 0x0000800000000001,
+
+    // these values taken from upstream testsuite
+    0x00000000000f4240,
+    0x00000000016e3600,
+    0x0000008cdcdea440,
+
+    // borderline between subnormal and normal
+    0x000ffffffffffff0, 0x000ffffffffffff1,
+    0x000ffffffffffffe, 0x000fffffffffffff,
+
+    // additional tests
+    0x0000000000000000,
+
+    // smallest normal values
+    0x0010000000000000, 0x0010000000000001,
+    0x0010000000000002, 0x0018000000000000,
+
+    0x3ddb7cdfd9d7bdba, 0x3ddb7cdfd9d7bdbb, 0x3ddb7cdfd9d7bdbc,
+    0x3e112e0be826d694, 0x3e112e0be826d695, 0x3e112e0be826d696,
+    0x3e45798ee2308c39, 0x3e45798ee2308c3a, 0x3e45798ee2308c3b,
+    0x3e7ad7f29abcaf47, 0x3e7ad7f29abcaf48, 0x3e7ad7f29abcaf49,
+    0x3eb0c6f7a0b5ed8c, 0x3eb0c6f7a0b5ed8d, 0x3eb0c6f7a0b5ed8e,
+    0x3ee4f8b588e368ef, 0x3ee4f8b588e368f0, 0x3ee4f8b588e368f1,
+    0x3f1a36e2eb1c432c, 0x3f1a36e2eb1c432d, 0x3f1a36e2eb1c432e,
+    0x3f50624dd2f1a9fb, 0x3f50624dd2f1a9fc, 0x3f50624dd2f1a9fd,
+    0x3f847ae147ae147a, 0x3f847ae147ae147b, 0x3f847ae147ae147c,
+    0x3fb9999999999999, 0x3fb999999999999a, 0x3fb999999999999b,
+
+    // values very close to 1
+    0x3feffffffffffff0, 0x3feffffffffffff1, 0x3feffffffffffff2,
+    0x3feffffffffffff3, 0x3feffffffffffff4, 0x3feffffffffffff5,
+    0x3feffffffffffff6, 0x3feffffffffffff7, 0x3feffffffffffff8,
+    0x3feffffffffffff9, 0x3feffffffffffffa, 0x3feffffffffffffb,
+    0x3feffffffffffffc, 0x3feffffffffffffd, 0x3feffffffffffffe,
+    0x3fefffffffffffff,
+    0x3ff0000000000000,
+    0x3ff0000000000001, 0x3ff0000000000002, 0x3ff0000000000003,
+    0x3ff0000000000004, 0x3ff0000000000005, 0x3ff0000000000006,
+    0x3ff0000000000007, 0x3ff0000000000008, 0x3ff0000000000009,
+
+    0x3ff921fb54442d18,
+    0x4005bf0a8b14576a,
+    0x400921fb54442d18,
+
+    0x4023ffffffffffff, 0x4024000000000000, 0x4024000000000001,
+    0x4058ffffffffffff, 0x4059000000000000, 0x4059000000000001,
+    0x408f3fffffffffff, 0x408f400000000000, 0x408f400000000001,
+    0x40c387ffffffffff, 0x40c3880000000000, 0x40c3880000000001,
+    0x40f869ffffffffff, 0x40f86a0000000000, 0x40f86a0000000001,
+    0x412e847fffffffff, 0x412e848000000000, 0x412e848000000001,
+    0x416312cfffffffff, 0x416312d000000000, 0x416312d000000001,
+    0x4197d783ffffffff, 0x4197d78400000000, 0x4197d78400000001,
+    0x41cdcd64ffffffff, 0x41cdcd6500000000, 0x41cdcd6500000001,
+    0x4202a05f1fffffff, 0x4202a05f20000000, 0x4202a05f20000001,
+    0x42374876e7ffffff, 0x42374876e8000000, 0x42374876e8000001,
+    0x426d1a94a1ffffff, 0x426d1a94a2000000, 0x426d1a94a2000001,
+    0x42a2309ce53fffff, 0x42a2309ce5400000, 0x42a2309ce5400001,
+    0x42d6bcc41e8fffff, 0x42d6bcc41e900000, 0x42d6bcc41e900001,
+    0x430c6bf52633ffff, 0x430c6bf526340000, 0x430c6bf526340001,
+    0x4341c37937e07fff, 0x4341c37937e08000, 0x4341c37937e08001,
+    0x4376345785d89fff, 0x4376345785d8a000, 0x4376345785d8a001,
+    0x43abc16d674ec7ff, 0x43abc16d674ec800, 0x43abc16d674ec801,
+    0x43e158e460913cff, 0x43e158e460913d00, 0x43e158e460913d01,
+    0x4415af1d78b58c3f, 0x4415af1d78b58c40, 0x4415af1d78b58c41,
+    0x444b1ae4d6e2ef4f, 0x444b1ae4d6e2ef50, 0x444b1ae4d6e2ef51,
+    0x4480f0cf064dd591, 0x4480f0cf064dd592, 0x4480f0cf064dd593,
+    0x44b52d02c7e14af5, 0x44b52d02c7e14af6, 0x44b52d02c7e14af7,
+    0x44ea784379d99db3, 0x44ea784379d99db4, 0x44ea784379d99db5,
+    0x45208b2a2c280290, 0x45208b2a2c280291, 0x45208b2a2c280292,
+
+    0x7feffffffffffffe, 0x7fefffffffffffff,
+
+    // round to even tests (+ve)
+    0x4350000000000002,
+    0x4350000000002e06,
+    0x4352000000000003,
+    0x4352000000000004,
+    0x4358000000000003,
+    0x4358000000000004,
+    0x435f000000000020,
+
+    // round to even tests (-ve)
+    0xc350000000000002,
+    0xc350000000002e06,
+    0xc352000000000003,
+    0xc352000000000004,
+    0xc358000000000003,
+    0xc358000000000004,
+    0xc35f000000000020,
+
+    // exercise fixed-point memmoves
+    0x42dc12218377de66,
+    0x42a674e79c5fe51f,
+    0x4271f71fb04cb74c,
+    0x423cbe991a145879,
+    0x4206fee0e1a9e061,
+    0x41d26580b487e6b4,
+    0x419d6f34540ca453,
+    0x41678c29dcd6e9dc,
+    0x4132d687e3df217d,
+    0x40fe240c9fcb68c8,
+    0x40c81cd6e63c53d3,
+    0x40934a4584fd0fdc,
+    0x405edd3c07fb4c93,
+    0x4028b0fcd32f7076,
+    0x3ff3c0ca428c59f8,
+
+    // these cases come from the upstream's testsuite
+    // LotsOfTrailingZeros)
+    0x3e60000000000000,
+
+    // Regression
+    0xc352bd2668e077c4,
+    0x434018601510c000,
+    0x43d055dc36f24000,
+    0x43e052961c6f8000,
+    0x3ff3c0ca2a5b1d5d,
+
+    // LooksLikePow5
+    0x4830f0cf064dd592,
+    0x4840f0cf064dd592,
+    0x4850f0cf064dd592,
+
+    // OutputLength
+    0x3ff3333333333333,
+    0x3ff3ae147ae147ae,
+    0x3ff3be76c8b43958,
+    0x3ff3c083126e978d,
+    0x3ff3c0c1fc8f3238,
+    0x3ff3c0c9539b8887,
+    0x3ff3c0ca2a5b1d5d,
+    0x3ff3c0ca4283de1b,
+    0x3ff3c0ca43db770a,
+    0x3ff3c0ca428abd53,
+    0x3ff3c0ca428c1d2b,
+    0x3ff3c0ca428c51f2,
+    0x3ff3c0ca428c58fc,
+    0x3ff3c0ca428c59dd,
+    0x3ff3c0ca428c59f8,
+    0x3ff3c0ca428c59fb,
+
+    // 32-bit chunking
+    0x40112e0be8047a7d,
+    0x40112e0be815a889,
+    0x40112e0be826d695,
+    0x40112e0be83804a1,
+    0x40112e0be84932ad,
+
+    // MinMaxShift
+    0x0040000000000000,
+    0x007fffffffffffff,
+    0x0290000000000000,
+    0x029fffffffffffff,
+    0x4350000000000000,
+    0x435fffffffffffff,
+    0x1330000000000000,
+    0x133fffffffffffff,
+    0x3a6fa7161a4d6e0c
+  };
+
+  for (int i = 0; i < sizeof hex_values / sizeof hex_values[0]; ++i) {
+    auto hex_value = hex_values[i];
+    double dbl = *(reinterpret_cast<double*>(&hex_value));
+    char buf[100];
+    snprintf(buf, sizeof buf, "%.17g", dbl);
+    BOOST_TEST_MESSAGE ("hex_value: " << std::hex << hex_value << std::dec << ", value: " << buf );
+
+    double dbl_from_database = 0.0;
+    std::string exception_msg;
+
+    try {
+      {
+        dbo::Transaction t(*f.session_);
+        auto a = f.session_->addNew<A>();
+        a.modify()->i = i;
+        a.modify()->d = dbl;
+      }
+
+#ifdef POSTGRES
+      {
+        dbo::Transaction t(*f.session_);
+        std::string dbl_from_database_s = f.session_->query<std::string>(
+          "SELECT \"d\" FROM " SCHEMA "\"table_a\" WHERE \"i\"=?"
+        ).bind(i).resultValue();
+        BOOST_TEST_MESSAGE ("PG text value: " << dbl_from_database_s );
+      }
+#endif
+
+      {
+        dbo::Transaction t(*f.session_);
+        auto a = f.session_->find<A>().where("\"i\" = ?").bind(i).resultValue();
+        dbl_from_database = a->d;
+      }
+    } catch (std::exception& e) {
+      exception_msg = std::string("unexpected exception: ") + e.what();
+    }
+
+    BOOST_TEST(exception_msg.empty(), exception_msg);
+    if (exception_msg.empty()) {
+      if (std::isnan(dbl)) {
+        BOOST_CHECK(std::isnan(dbl_from_database));
+      }
+      else if (std::isinf(dbl)) {
+        BOOST_CHECK(std::isinf(dbl_from_database));
+        BOOST_CHECK_EQUAL(dbl_from_database, dbl);
+      }
+      else if (std::fpclassify(dbl) == FP_SUBNORMAL) {
+        // kludge to improve warning (highlighting that it is a subnormal)
+        double subnormal_dbl = dbl;
+        BOOST_CHECK_CLOSE(dbl_from_database, subnormal_dbl, 2e-14);
+        // only warn if subnormals do not match, since implementations/support may vary
+        BOOST_WARN_EQUAL(dbl_from_database, subnormal_dbl);
+      }
+      else {
+        BOOST_CHECK_CLOSE(dbl_from_database, dbl, 2e-14);
+        BOOST_CHECK_EQUAL(dbl_from_database, dbl);
+      }
+    }
+  }
+#endif
+}
+
 BOOST_AUTO_TEST_SUITE_END()
-- 
2.33.1

