#include "maths.hpp" #include "tap.hpp" #include #include #include using std::sqrt; using std::numeric_limits; void test_comparisons (cruft::TAP::logger &tap) { // Check pos/neg zeroes tap.expect (cruft::almost_equal ( 0.f, 0.f), "equal float zeros +ve/+ve"); tap.expect (cruft::almost_equal ( 0.f, -0.f), "equal float zeros +ve/-ve"); tap.expect (cruft::almost_equal (-0.f, 0.f), "equal float zeros -ve/+ve"); tap.expect (cruft::almost_equal (-0.f, -0.f), "equal float zeros -ve/-ve"); tap.expect (cruft::almost_equal ( 0., 0.), "equal double zeroes +ve/+ve"); tap.expect (cruft::almost_equal ( 0., -0.), "equal double zeroes +ve/+ve"); tap.expect (cruft::almost_equal (-0., 0.), "equal double zeroes +ve/+ve"); tap.expect (cruft::almost_equal (-0., -0.), "equal double zeroes +ve/+ve"); // Check zero comparison with values near the expected cutoff tap.expect (cruft::almost_zero (1e-45f), "almost_zero with low value"); tap.expect (!cruft::almost_zero (1e-40f), "not almost_zero with low value"); tap.expect (!cruft::exactly_zero (1e-45f), "not exactly_zero with low value"); // Compare values a little away from zero tap.expect (!cruft::almost_equal (-2.0, 0.0), "not equal floats"); tap.expect (!cruft::almost_equal (-2.f, 0.f), "not equal doubles"); // Compare values at the maximum extreme tap.expect (!cruft::almost_equal (-std::numeric_limits::max (), 0.f), "not equal -max/0 float"); tap.expect (!cruft::almost_equal (-std::numeric_limits::max (), std::numeric_limits::max ()), "not equal -max/max"); // Compare infinity values tap.expect ( cruft::almost_equal (numeric_limits::infinity (), numeric_limits::infinity ()), "almost_equal +infinity"); tap.expect (!cruft::almost_equal (numeric_limits::infinity (), 0.0), "not almost_equal +inf/0"); // Compare NaNs tap.expect (!cruft::almost_equal (0., numeric_limits::quiet_NaN ()), "not almost_equal double 0/NaN"); tap.expect (!cruft::almost_equal (numeric_limits::quiet_NaN (), 0.), "not almost_equal double NaN/0"); tap.expect (!cruft::almost_equal (numeric_limits::quiet_NaN (), numeric_limits::quiet_NaN ()), "not almost_equal NaN/NaN"); // Compare reasonably close values that are wrong tap.expect (!cruft::almost_equal (1.0000f, 1.0001f), ".0001f difference inequality"); tap.expect ( cruft::almost_equal (1.0000f, 1.00001f), ".00001f difference inequality"); } void test_normalisations (cruft::TAP::logger &tap) { // u8 to float { auto a = cruft::renormalise (255); tap.expect_eq (a, 1.f, "normalise uint8 max"); auto b = cruft::renormalise (0); tap.expect_eq (b, 0.f, "normalise uint8 min"); } // float to u8 { bool success = true; static const struct { float a; uint8_t b; } TESTS[] = { { 1.f, 255 }, { 0.5f, 127 }, { 2.f, 255 }, { -1.f, 0 } }; for (auto i: TESTS) { auto v = cruft::renormalise (i.a); success = success && cruft::almost_equal (unsigned{v}, unsigned{i.b}); } tap.expect (success, "float-u8 normalisation"); } // float to uint32 // exercises an integer type that has more precision than float { bool success = true; static const struct { float a; uint32_t b; } TESTS[] { { 0.f, 0x00000000 }, // lo range { 1.f, 0xffffffff }, // hi range { 0.5f, 0x7fffff7f }, // 31 bits { 0.001953125f, 0x007fff00 }, // 23 bits }; for (auto t: TESTS) { auto v = cruft::renormalise (t.a); success = success && cruft::almost_equal (t.b, v); } tap.expect (success, "float-u32 normalisation"); } tap.expect_eq (cruft::renormalise (0xff), 0xffffffffu, "normalise hi u8-to-u32"); tap.expect_eq (cruft::renormalise (0x00), 0x00000000u, "normalise lo u8-to-u32"); tap.expect_eq (cruft::renormalise (0xffffffff), 0xffu, "normalise hi u32-to-u8"); // sint32 to uint32 { static const struct { int32_t sint; uint32_t uint; const char *msg; } TESTS[] = { { std::numeric_limits::min (), std::numeric_limits::min (), "min" }, { std::numeric_limits::max (), std::numeric_limits::max (), "max" }, { 0, std::numeric_limits::max () / 2u + 1, "mid" }, }; for (const auto &t: TESTS) { tap.expect_eq (cruft::renormalise (t.sint), t.uint, "{:s} s32-to-u32", t.msg); tap.expect_eq (cruft::renormalise (t.uint), t.sint, "{:s} u32-to-s32", t.msg); } } } #include int main (void) { cruft::TAP::logger tap; // Max out the precision in case we trigger debug output std::cerr.precision (std::numeric_limits::digits10); std::cout.precision (std::numeric_limits::digits10); test_comparisons (tap); test_normalisations (tap); tap.expect_eq (cruft::min (-2, 0, 2), -2, "variadic min"); tap.expect_eq (cruft::max (-2, 0, 2), 2, "variadic max"); tap.expect_eq (cruft::digits10( 0), 1, "digits10, 0"); tap.expect_eq (cruft::digits10( 1), 1, "digits10, 1"); tap.expect_eq (cruft::digits10(10), 2, "digits10, 10"); tap.expect_eq (cruft::pow (4u,2), 16u, "pow2"); static const float POS_ZERO = 1.f / numeric_limits::infinity (); union { uint32_t neg_zero_bits = 0x80000000; float NEG_ZERO; }; tap.expect_eq (cruft::sign (-1), -1, "sign(-1)"); tap.expect_eq (cruft::sign ( 1), 1, "sign( 1)"); tap.expect_eq (cruft::sign (POS_ZERO), 1.f, "sign (POS_ZERO)"); tap.expect_eq (cruft::sign (NEG_ZERO), -1.f, "sign (NEG_ZERO)"); tap.expect_eq (cruft::sign ( numeric_limits::infinity ()), 1., "sign +inf"); tap.expect_eq (cruft::sign (-numeric_limits::infinity ()), -1., "sign -inf"); tap.expect_eq (cruft::to_degrees (cruft::pi< float>), 180.f, "to_degrees float"); tap.expect_eq (cruft::to_degrees (cruft::pi), 180.0, "to_degrees double"); tap.expect_eq (cruft::to_radians (180.f), cruft::pi, "to_radians float"); tap.expect_eq (cruft::to_radians (180.0), cruft::pi, "to_radians double"); tap.expect_eq (cruft::log2 (8u), 3u, "log2 +ve"); tap.expect_eq (cruft::log2 (1u), 0u, "log2 zero"); //tap.expect_eq (log2 (9u), 3, "log2up 9"); tap.expect_eq (cruft::log2up (9u), 4u, "log2up 9"); tap.expect_eq (cruft::log2up (8u), 3u, "log2up 9"); tap.expect_eq (cruft::log2up (1u), 0u, "log2up 9"); return tap.status (); }