#include "testing/testing.hpp" #include "base/math.hpp" #include #include namespace math_test { using namespace math; namespace { // Returns the next representable floating point value without using conversion to integer. template Float NextFloat(Float const x, int dir = 1) { return boost::math::float_advance(x, dir); } template void TestMaxULPs() { for (unsigned int logMaxULPs = 0; logMaxULPs <= 8; ++logMaxULPs) { unsigned int const maxULPs = (1 << logMaxULPs) - 1; for (int base = -1; base <= 1; ++base) { for (int dir = -1; dir <= 1; dir += 2) { Float const x = base; Float y = x; for (unsigned int i = 0; i <= maxULPs; ++i) { TEST(AlmostEqualULPs(x, y, maxULPs), (x, y, maxULPs, x - y, dir)); Float const nextY = NextFloat(y, dir); TEST_NOT_EQUAL(y, nextY, (i, base, dir)); y = nextY; } TEST(!AlmostEqualULPs(x, y, maxULPs), (x, y, maxULPs, x - y)); } } } } } // namespace UNIT_TEST(round_lround_smoke) { TEST_EQUAL(std::round(0.4), 0.0, ()); TEST_EQUAL(std::round(0.6), 1.0, ()); TEST_EQUAL(std::round(-0.4), 0.0, ()); TEST_EQUAL(std::round(-0.6), -1.0, ()); TEST_EQUAL(std::lround(0.4), 0l, ()); TEST_EQUAL(std::lround(0.6), 1l, ()); TEST_EQUAL(std::lround(-0.4), 0l, ()); TEST_EQUAL(std::lround(-0.6), -1l, ()); } UNIT_TEST(PowUInt) { TEST_EQUAL(PowUint(3, 10), 59049, ()); } UNIT_TEST(AlmostEqualULPs_double) { TEST_ALMOST_EQUAL_ULPS(3.0, 3.0, ()); TEST_ALMOST_EQUAL_ULPS(+0.0, -0.0, ()); double constexpr eps = std::numeric_limits::epsilon(); double constexpr dmax = std::numeric_limits::max(); TEST_ALMOST_EQUAL_ULPS(1.0 + eps, 1.0, ()); TEST_ALMOST_EQUAL_ULPS(1.0 - eps, 1.0, ()); TEST_ALMOST_EQUAL_ULPS(1.0 - eps, 1.0 + eps, ()); TEST_ALMOST_EQUAL_ULPS(dmax, dmax, ()); TEST_ALMOST_EQUAL_ULPS(-dmax, -dmax, ()); TEST_ALMOST_EQUAL_ULPS(dmax / 2.0, dmax / 2.0, ()); TEST_ALMOST_EQUAL_ULPS(1.0 / dmax, 1.0 / dmax, ()); TEST_ALMOST_EQUAL_ULPS(-1.0 / dmax, -1.0 / dmax, ()); TEST(!AlmostEqualULPs(1.0, -1.0), ()); TEST(!AlmostEqualULPs(2.0, -2.0), ()); TEST(!AlmostEqualULPs(dmax, -dmax), ()); // That's why AlmostEqualULPs is a strange function, IMHO. TEST(!AlmostEqualULPs(0.0, eps), ()); TEST(!AlmostEqualULPs(-eps, 0.0), ()); TEST(!AlmostEqualULPs(eps, 2.0 * eps), ()); } UNIT_TEST(AlmostEqualULPs_float) { TEST_ALMOST_EQUAL_ULPS(3.0f, 3.0f, ()); TEST_ALMOST_EQUAL_ULPS(+0.0f, -0.0f, ()); float const eps = std::numeric_limits::epsilon(); float const dmax = std::numeric_limits::max(); TEST_ALMOST_EQUAL_ULPS(1.0f + eps, 1.0f, ()); TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f, ()); TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f + eps, ()); TEST_ALMOST_EQUAL_ULPS(dmax, dmax, ()); TEST_ALMOST_EQUAL_ULPS(-dmax, -dmax, ()); TEST_ALMOST_EQUAL_ULPS(dmax / 2.0f, dmax / 2.0f, ()); TEST_ALMOST_EQUAL_ULPS(1.0f / dmax, 1.0f / dmax, ()); TEST_ALMOST_EQUAL_ULPS(-1.0f / dmax, -1.0f / dmax, ()); TEST(!AlmostEqualULPs(1.0f, -1.0f), ()); TEST(!AlmostEqualULPs(2.0f, -2.0f), ()); TEST(!AlmostEqualULPs(dmax, -dmax), ()); // That's why AlmostEqualULPs is a strange function, IMHO. TEST(!AlmostEqualULPs(0.0f, eps), ()); TEST(!AlmostEqualULPs(-eps, 0.0f), ()); TEST(!AlmostEqualULPs(eps, 2.0f * eps), ()); } UNIT_TEST(AlmostEqual_Smoke) { double constexpr small = 1e-18; double constexpr eps = 1e-10; TEST(AlmostEqualAbs(0.0, 0.0 + small, eps), ()); TEST(!AlmostEqualRel(0.0, 0.0 + small, eps), ()); TEST(!AlmostEqualULPs(0.0, 0.0 + small), ()); TEST(AlmostEqualAbs(1.0, 1.0 + small, eps), ()); TEST(AlmostEqualRel(1.0, 1.0 + small, eps), ()); TEST(AlmostEqualULPs(1.0, 1.0 + small), ()); TEST(AlmostEqualRel(123456789.0, 123456780.0, 1e-7), ()); } UNIT_TEST(AlmostEqualULPs_MaxULPs_double) { TestMaxULPs(); } UNIT_TEST(AlmostEqualULPs_MaxULPs_float) { TestMaxULPs(); } UNIT_TEST(TEST_FLOAT_DOUBLE_EQUAL_macros) { float const fx = 3; float const fy = NextFloat(NextFloat(NextFloat(fx))); TEST_ALMOST_EQUAL_ULPS(fx, fy, ()); TEST_NOT_ALMOST_EQUAL_ULPS(fx, 2.0f, ()); double const dx = 3; double const dy = NextFloat(NextFloat(NextFloat(dx))); TEST_ALMOST_EQUAL_ULPS(dx, dy, ()); TEST_NOT_ALMOST_EQUAL_ULPS(dx, 2.0, ()); } UNIT_TEST(GCD) { TEST_EQUAL(GCD(6, 3), 3, ()); TEST_EQUAL(GCD(14, 7), 7, ()); TEST_EQUAL(GCD(100, 100), 100, ()); TEST_EQUAL(GCD(7, 3), 1, ()); TEST_EQUAL(GCD(8, 3), 1, ()); TEST_EQUAL(GCD(9, 3), 3, ()); } UNIT_TEST(LCM) { TEST_EQUAL(LCM(6, 3), 6, ()); TEST_EQUAL(LCM(14, 7), 14, ()); TEST_EQUAL(LCM(100, 100), 100, ()); TEST_EQUAL(LCM(7, 3), 21, ()); TEST_EQUAL(LCM(8, 3), 24, ()); TEST_EQUAL(LCM(9, 3), 9, ()); } UNIT_TEST(Sign) { TEST_EQUAL(1, Sign(1), ()); TEST_EQUAL(1, Sign(10.4), ()); TEST_EQUAL(0, Sign(0), ()); TEST_EQUAL(0, Sign(0.0), ()); TEST_EQUAL(-1, Sign(-11), ()); TEST_EQUAL(-1, Sign(-10.4), ()); } UNIT_TEST(is_finite) { static_assert(std::numeric_limits::has_infinity); static_assert(std::numeric_limits::has_quiet_NaN); using math::is_finite, math::Nan, math::Infinity; TEST(!is_finite(Nan()), ()); TEST(!is_finite(Infinity()), ()); TEST(!is_finite(DBL_MAX * 2.0), ()); TEST(is_finite(0.0), ()); TEST(is_finite(1.0), ()); TEST(is_finite(-2.0), ()); TEST(is_finite(DBL_MIN), ()); TEST(is_finite(DBL_MAX), ()); TEST(is_finite(DBL_MIN / 2.0), ("As in cppreference example")); } UNIT_TEST(iround) { TEST_EQUAL(iround(0.0), 0, ()); TEST_EQUAL(iround(1.0), 1, ()); TEST_EQUAL(iround(1.5), 2, ()); TEST_EQUAL(iround(-1.5), -2, ()); TEST_EQUAL(iround(2.5), 3, ()); TEST_EQUAL(iround(-2.5), -3, ()); TEST_EQUAL(iround(2.5f), 3, ()); TEST_EQUAL(iround(-2.5f), -3, ()); TEST_EQUAL(iround(double(std::numeric_limits::max()) - 0.5), std::numeric_limits::max(), ()); TEST_EQUAL(iround(double(std::numeric_limits::min()) + 0.5), std::numeric_limits::min(), ()); } } // namespace math_test