diff --git a/test/floating_point_test.cpp b/test/floating_point_test.cpp index d771bee..463a968 100644 --- a/test/floating_point_test.cpp +++ b/test/floating_point_test.cpp @@ -15,11 +15,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include using namespace boost::endian; using std::cout; @@ -31,6 +34,7 @@ using std::numeric_limits; namespace { + // to_big() and to_little() provide convenient independent functions for // creating test values of known endianness. @@ -52,6 +56,49 @@ namespace return x; } + template + struct test_case + { + std::string desc; + T value; // native value and representation + std::string big; // as hex characters; invariant size() == 2*sizeof(T) + std::string little; // as hex characters; invariant size() == 2*sizeof(T) + // big and little endian expected values are held as strings so representation is + // independent of platform endianness and readers do not have to perform mental + // gymnastics to reason about what the expected representation is for a platform. + + const test_case& assign(const std::string& desc_, const T& value_, + const std::string& big_, const std::string& little_) + { + desc = desc_; + std::memcpy(&value, &value_, sizeof(T)); // use memcpy in case copy assignment or + // copy construction alters representation + big = big_; + little = little_; + return *this; + } + }; + + // to_big_inplace() and to_little_inplace() provide convenient independent functions to + // aid creating test values of known endianness. They do so in place to avoid the + // possibility that floating point assignment or copy construction modifies the + // representation, such as for normalization. + + template + void to_big_inplace(T& x) + { +# ifdef BOOST_LITTLE_ENDIAN + std::reverse(reinterpret_cast(&x), reinterpret_cast(&x) + sizeof(T)); +# endif + } + + template + void to_little_inplace(T& x) + { +# ifdef BOOST_BIG_ENDIAN + std::reverse(reinterpret_cast(&x), reinterpret_cast(&x) + sizeof(T)); +# endif + } template std::string to_hex(const T& x) @@ -69,6 +116,71 @@ namespace return tmp; } + const std::size_t n_test_cases = 10; + boost::array, n_test_cases> float_test_cases; + boost::array, n_test_cases> double_test_cases; + + void build_auto_test_cases() + { + using namespace boost::math::constants; + int i = 0; + + float_test_cases[i].assign("numeric_limits::min()", + numeric_limits::min(), "00800000", "00008000"); + double_test_cases[i++].assign("numeric_limits::min()", + numeric_limits::min(), "0010000000000000", "0000000000001000"); + + float_test_cases[i].assign("numeric_limits::max()", + numeric_limits::max(), "7f7fffff", "ffff7f7f"); + double_test_cases[i++].assign("numeric_limits::max()", + numeric_limits::max(), "7fefffffffffffff", "ffffffffffffef7f"); + + float_test_cases[i].assign("numeric_limits::round_error()", + numeric_limits::round_error(), "3f000000", "0000003f"); + double_test_cases[i++].assign("numeric_limits::round_error()", + numeric_limits::round_error(), "3fe0000000000000", "000000000000e03f"); + + float_test_cases[i].assign("numeric_limits::infinity()", + numeric_limits::infinity(), "7f800000", "0000807f"); + double_test_cases[i++].assign("numeric_limits::infinity()", + numeric_limits::infinity(), "7ff0000000000000", "000000000000f07f"); + + float_test_cases[i].assign("-numeric_limits::infinity()", + -numeric_limits::infinity(), "ff800000", "000080ff"); + double_test_cases[i++].assign("-numeric_limits::infinity()", + -numeric_limits::infinity(), "fff0000000000000", "000000000000f0ff"); + + float_test_cases[i].assign("numeric_limits::quiet_NaN()", + numeric_limits::quiet_NaN(), "7fc00000", "0000c07f"); + double_test_cases[i++].assign("numeric_limits::quiet_NaN()", + numeric_limits::quiet_NaN(), "7ff8000000000000", "000000000000f87f"); + + float_test_cases[i].assign("numeric_limits::signaling_NaN()", + numeric_limits::signaling_NaN(), "7fc00001", "0100c07f"); + double_test_cases[i++].assign("numeric_limits::signaling_NaN()", + numeric_limits::signaling_NaN(), "7ff8000000000001", "010000000000f87f"); + + float_test_cases[i].assign("numeric_limits::denorm_min()", + numeric_limits::denorm_min(), "00000001", "01000000"); + double_test_cases[i++].assign("numeric_limits::denorm_min()", + numeric_limits::denorm_min(), "0000000000000001", "0100000000000000"); + + float_test_cases[i].assign("pi()", + pi(), "40490fdb", "db0f4940"); + double_test_cases[i++].assign("pi()", + pi(), "400921fb54442d18", "182d4454fb210940"); + + uint32_t vf1 (0x12345678U); + float_test_cases[i].assign("native uint32_t 0x12345678U as float", + *reinterpret_cast(&vf1), "12345678", "78563412"); + uint64_t vd1 (0x0123456789abcdefULL); + double_test_cases[i++].assign("native uint64_t 0x0123456789abcdefULL as double", + *reinterpret_cast(&vd1), "0123456789abcdef", "efcdab8967452301"); + + BOOST_ASSERT(i == n_test_cases); + } + + template void show_value(const char* desc, const T& value) { @@ -81,6 +193,7 @@ namespace template void report_limits(const char* type) { + using namespace boost::math::constants; cout << "\nHeader values for std::numeric_limits<" << type << ">\n\n"; cout << " is_specialized " << numeric_limits::is_specialized << "\n"; cout << " is_signed " << numeric_limits::is_signed << "\n"; @@ -116,6 +229,7 @@ namespace show_value("denorm_min()", numeric_limits::denorm_min()); show_value("0.0", static_cast(0.0)); show_value("1.0", static_cast(1.0)); + show_value("pi()", pi()); } template @@ -202,6 +316,19 @@ namespace BOOST_TEST_MEM_EQ(native_to_little(1.0), to_little(0x3ff0000000000000ULL)); } +template +void auto_test(const char* msg, const boost::array, n_test_cases>& cases) +{ + cout << "auto test " << msg << " ..." << endl; + + for (int i = 0; i < n_test_cases; ++i) + { + cout << " " << cases[i].desc << endl; + BOOST_TEST_EQ(to_hex(native_to_big(cases[i].value)), cases[i].big); + BOOST_TEST_EQ(to_hex(native_to_little(cases[i].value)), cases[i].little); + } +} + } // unnamed namespace //--------------------------------------------------------------------------------------// @@ -225,6 +352,10 @@ int cpp_main(int, char *[]) double_value_test(); round_trip_test("double"); + build_auto_test_cases(); + auto_test("float", float_test_cases); + auto_test("double", double_test_cases); + cout << "\n done" << endl; return ::boost::endian::report_errors();