I need a changing value that can be manually stepped with step()
that goes back and forth a min
and a max
, moving by speed
every step()
.
This is my current code:
template<typename T> struct PingPongValue {
T value, min, max, speed, dir{1};
PingPongValue(T mMin, T mMax, T mSpeed)
: value(mMin), min(mMin), max(mMax), speed(mSpeed) { }
void step()
{
value += speed * dir;
if(value > max) { value = max; dir = -1; }
else if(value < min) { value = min; dir = +1; }
}
};
Example:
PingPongValue v{0, 5, 1};
v.step(); // v.value == 1
v.step(); // v.value == 2
v.step(); // v.value == 3
v.step(); // v.value == 4
v.step(); // v.value == 5
v.step(); // v.value == 4
v.step(); // v.value == 3
v.step(); // v.value == 2
// etc...
I suppose there's a mathematical way to represent this as a branchless function, but I cannot figure it out. I tried using modulo but I still need a dir
variable to change step direction.
You can do it with an array, something like this (WARNING: probably has off-by-one errors galore!):
int total_steps = 2*(max - min + 1)/speed; // this may be wrong -- have to double check
T steps[total_steps];
for(int i = 0; i < max - min; ++i)
steps[total_steps - i] = steps[i] = min + i*speed;
Then you can use modulo total_steps
to step through the array forever.
I'm not a fan of "branchless" algorithms, as many times they're just used "because branching is slow", and in that case, IMO it's the optimizer's job to find a faster way.
Nevertheless, you could use comparisons, as bools yield either 0
or 1
when converted to an integral type. Whether that's branchless depends on the architecture AFAIK.
value += speed*dir; // allowing over-/underflow
value += (min-value)*(value<min) + (max-value)*(value>max); // clamp
dir += 2* ((value==min) - (value==max)); // set dir
SSCCE:
template<typename T> struct PingPongValue {
T value, min, max, speed, dir{1};
PingPongValue(T mMin, T mMax, T mSpeed)
: value(mMin), min(mMin), max(mMax), speed(mSpeed) { }
void step()
{
// allowing over-/underflow
value += speed*dir;
// clamp
value += (min-value)*(value<min) + (max-value)*(value>max);
// set dir
dir += 2* ((value==min) - (value==max));
}
};
#include <iostream>
template<class T>
void step(PingPongValue<T>& v)
{
v.step();
std::cout << "stepped to: " << v.value << std::endl;
}
int main()
{
PingPongValue<int> p{-3, 6, 2};
std::cout << "initial: " << p.value << std::endl;
for(int i = 0; i < 10; ++i)
{
step(p);
}
}
Output:
initial: -3 stepped to: -1 stepped to: 1 stepped to: 3 stepped to: 5 stepped to: 6 stepped to: 4 stepped to: 2 stepped to: 0 stepped to: -2 stepped to: -3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With