co-maps/libs/base/base_tests/math_test.cpp
2025-11-22 13:58:55 +01:00

228 lines
6 KiB
C++

#include "testing/testing.hpp"
#include "base/math.hpp"
#include <limits>
#include <boost/math/special_functions/next.hpp>
namespace math_test
{
using namespace math;
namespace
{
// Returns the next representable floating point value without using conversion to integer.
template <typename Float>
Float NextFloat(Float const x, int dir = 1)
{
return boost::math::float_advance(x, dir);
}
template <typename Float>
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<double>::epsilon();
double constexpr dmax = std::numeric_limits<double>::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<float>::epsilon();
float const dmax = std::numeric_limits<float>::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<double>();
}
UNIT_TEST(AlmostEqualULPs_MaxULPs_float)
{
TestMaxULPs<float>();
}
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<double>::has_infinity);
static_assert(std::numeric_limits<double>::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<int>::max()) - 0.5), std::numeric_limits<int>::max(), ());
TEST_EQUAL(iround(double(std::numeric_limits<int>::min()) + 0.5), std::numeric_limits<int>::min(), ());
}
} // namespace math_test