BLI: support mod_periodic on integer types

See #125876 for more details.

Pull Request: https://projects.blender.org/blender/blender/pulls/125876
This commit is contained in:
Iliya Katushenock
2024-09-17 15:48:06 +02:00
committed by Jacques Lucke
parent b0a9e45cb5
commit dceccfda5a
2 changed files with 44 additions and 8 deletions

View File

@@ -103,19 +103,19 @@ template<typename T> inline T round(const T &a)
/**
* Repeats the saw-tooth pattern even on negative numbers.
* ex: `mod_periodic(-3, 4) = 1`, `mod(-3, 4)= -3`
* ex: `mod_periodic(-3, 4) = 1`, `mod(-3, 4)= -3`. This will cause undefined behavior for negative
* b.
*/
template<typename T> inline T mod_periodic(const T &a, const T &b)
{
BLI_assert(b > 0);
if constexpr (std::is_integral_v<T>) {
BLI_assert(std::numeric_limits<T>::max() - math::abs(a) >= b);
return ((a % b) + b) % b;
}
return a - (b * math::floor(a / b));
}
template<> inline int64_t mod_periodic(const int64_t &a, const int64_t &b)
{
int64_t c = (a >= 0) ? a : (-1 - a);
int64_t tmp = c - (b * (c / b));
/* Negative integers have different rounding that do not match floor(). */
return (a >= 0) ? tmp : (b - 1 - tmp);
}
template<typename T> inline T ceil(const T &a)
{

View File

@@ -211,4 +211,40 @@ TEST(math_base, FlooredFMod)
EXPECT_FLOAT_EQ(floored_fmod(12345.0f, 12345.0f), 0.0f);
}
TEST(math_base, ModPeriodic)
{
EXPECT_FLOAT_EQ(math::mod_periodic(3.27f, 1.57f), 0.12999988f);
EXPECT_FLOAT_EQ(math::mod_periodic(327.f, 47.f), 45.f);
EXPECT_FLOAT_EQ(math::mod_periodic(-0.1f, 1.0f), 0.9f);
EXPECT_FLOAT_EQ(math::mod_periodic(-0.9f, 1.0f), 0.1f);
EXPECT_FLOAT_EQ(math::mod_periodic(-100.1f, 1.0f), 0.90000153f);
EXPECT_FLOAT_EQ(math::mod_periodic(-0.1f, 12345.0f), 12344.9f);
EXPECT_FLOAT_EQ(math::mod_periodic(12345.1f, 12345.0f), 0.099609375f);
EXPECT_FLOAT_EQ(math::mod_periodic(12344.999f, 12345.0f), 12344.999f);
EXPECT_FLOAT_EQ(math::mod_periodic(12345.0f, 12345.0f), 0.0f);
EXPECT_EQ(math::mod_periodic(1, 10), 1);
EXPECT_EQ(math::mod_periodic(11, 10), 1);
EXPECT_EQ(math::mod_periodic(-1, 10), 9);
EXPECT_EQ(math::mod_periodic(-11, 10), 9);
EXPECT_EQ(math::mod_periodic(1, 1), 0);
EXPECT_EQ(math::mod_periodic(0, 99999), 0);
EXPECT_EQ(math::mod_periodic(99999, 99999), 0);
EXPECT_EQ(
math::mod_periodic(std::numeric_limits<int>::max() / 2, std::numeric_limits<int>::max() / 2),
0);
EXPECT_EQ(
math::mod_periodic(std::numeric_limits<int>::min() / 2, std::numeric_limits<int>::max() / 2),
std::numeric_limits<int>::max() / 2 - 1);
EXPECT_EQ(math::mod_periodic<int64_t>(1, 10), 1);
EXPECT_EQ(math::mod_periodic<int64_t>(11, 10), 1);
EXPECT_EQ(math::mod_periodic<int64_t>(-1, 10), 9);
EXPECT_EQ(math::mod_periodic<int64_t>(-11, 10), 9);
EXPECT_EQ(math::mod_periodic<int64_t>(1, 1), 0);
EXPECT_EQ(math::mod_periodic<int64_t>(0, 99999), 0);
EXPECT_EQ(math::mod_periodic<int64_t>(99999, 99999), 0);
}
} // namespace blender::tests