Reputation: 13484
I'm looking for the most succinct and general implementation of the following function:
float Constrain(float value, float min, float max);
Where Constrain() bounds value
in the range [min, float)
. Ie, the range includes min but excludes max
and values
greater than max
or less than min
wrap around in a circle. Ie, in a similar way to integers over/underflow.
The function should pass the following tests:
Constrain( 0.0, 0.0, 10.0) == 0.0
Constrain( 10.0, 0.0, 10.0) == 0.0
Constrain( 5.0, 0.0, 10.0) == 5.0
Constrain( 15.0, 0.0, 10.0) == 5.0
Constrain( -1.0, 0.0, 10.0) == 9.0
Constrain(-15.0, 0.0, 10.0) == 5.0
Constrain( 0.0, -5.0, 5.0) == 0.0
Constrain( 5.0, -5.0, 5.0) == -5.0
Constrain( 0.0, -5.0, 5.0) == 0.0
Constrain( 10.0, -5.0, 5.0) == 0.0
Constrain( -6.0, -5.0, 5.0) == 4.0
Constrain(-10.0, -5.0, 5.0) == 0.0
Constrain( 24.0, -5.0, 5.0) == 4.0
Constrain( 0.0, -5.0, 0.0) == -5.0
Constrain( 5.0, -5.0, 0.0) == -5.0
Constrain( 10.0, -5.0, 0.0) == -5.0
Constrain( -3.0, -5.0, 0.0) == -3.0
Constrain( -6.0, -5.0, 0.0) == -1.0
Constrain(-10.0, -5.0, 0.0) == -5.0
Note that the min
param can be assumed to be always numerically less than max
.
There is probably a very simple formula to solve this question but and I'm being spectacularly dumb not knowing the generalised solution to it.
Upvotes: 1
Views: 942
Reputation: 300
This also works:
double constrain(double value, double min, double max)
{
double Range = max - min;
if (value < min)
value = max - (max - value ) % (Range + 1); // Range+1 for inclusive
if (value > max)
value = (value - min) % (Range) + min; // Range(+0) for exclusive
return value;
}
Upvotes: 0
Reputation: 11
lrint() may be faster.
inline double fwrap(double x, double y)
{
return x - y * lrint(x / y - 0.5);
}
double constrain(double x, double lo, double hi)
{
return fwrap(x - lo, hi - lo) + lo;
}
Upvotes: 1
Reputation: 11
lrint() may be faster.
inline double fwrap(double x, double y)
{
return x - y * lrint(x / y - 0.5);
}
double constrain(double x, double lo, double hi)
{
return fwrap(x, hi - lo);
}
Upvotes: 1
Reputation: 19981
You're almost looking for the fmod
function. fmod(x,y)
returns the remainder on dividing x
by y
, both being double
s. The sign of the result is the same as that of x
(equivalently, the corresponding integer-part function is the one that rounds towards zero), and that's why it's only almost what you want. So, if x>=lo
then lo+fmod(x-lo,hi-lo)
is the Right Thing, but if x<lo
then hi+fmod(x-lo,hi-lo)
is oh-so-nearly the Right Thing except that when x<lo
and the result could be either lo
or hi
you get hi
instead of lo
.
So. You can split three ways:
double Constrain(x,lo,hi) {
double t = fmod(x-lo,hi-lo);
return t<0 ? t+hi : t+lo;
}
or you can use floor
instead [EDITED because the first version of this wasn't what I meant at all]:
double Constrain(x,lo,hi) {
double t = (x-lo) / (hi-lo);
return lo + (hi-lo) * (t-floor(t));
}
Take your pick if what you care about is comprehensibility; try them both if what you care about is performance.
Upvotes: 2