Reputation: 61
Is it possible to limit a value in a given range, between min and max, using only arithmetic? That is, +
-
x
/
and %
?
I am not able to use functions such as min
, max
nor IF
-statements.
Let's assume I have a range of [1850, 1880]
, for any values < 1850
, it should display 1850
. For values > 1880
, 1880
should be displayed. It would also be acceptable if only 1850
was displayed outside the range.
I tried:
x = (((x - xmax) % (xmax - xmin)) + (xmax - xmin)) % (xmax - xmin) + xmin
but it gives different values in the middle of the range for values lower than xmin
.
Upvotes: 2
Views: 2972
Reputation: 52
I found this solution in Python:
A = -1 # Minimum value
B = +1 # Maximum value
x = min(max(x, A), B)
Upvotes: -1
Reputation: 15035
If you know the size of the integer type, you can extract its sign bit (assuming two's complement) using integer division:
// Example in C
int sign_bit(int s)
{
// cast to unsigned (important)
unsigned u = (unsigned)s;
// number of bits in int
// if your integer size is fixed, this is just a constant
static const unsigned b = sizeof(int) * 8;
// pow(2, b - 1)
// again, a constant which can be pre-computed
static const unsigned p = 1 << (b - 1);
// use integer division to get top bit
return (int)(u / p);
}
This returns 1 if s < 0
and 0 otherwise; it can be used to calculate the absolute value:
int abs_arith(int v)
{
// sign bit
int b = sign_bit(v);
// actual sign (+1 / -1)
int s = 1 - 2 * b;
// sign(v) * v = abs(v)
return s * v;
}
The desired function looks like this:
It is useful to first shift the minimum to zero:
This function form can be computed as a sum of the two shifted absolute value functions below:
However the resultant function is scaled by a factor of 2; shifting to zero helps here because we only need to divide by 2, and shift back to the original minimum:
// Example in C
int clamp_minmax(int val, int min, int max)
{
// range length
int range = max - min;
// shift minimum to zero
val = val - min;
// blue function
int blue = abs_arith(val);
// green function
int green = range - abs_arith(val - range);
// add and divide by 2
val = (blue + green) / 2;
// shift to original minimum
return val + min;
}
This solution, although satisfies the requirements of the problem, is limited to signed integer types (and languages which allow integer overflow - I'm unsure of how this could be overcome in e.g. Java).
Upvotes: 5
Reputation: 763
I found this while messing around in... excel. It only works for strictly positive integers. Although this is not more restrictive as the answer by meowgoesthedog because he also effectivly halves the integer space by dividing by two at the end. It doesn't use mod.
//A = 1 if x <= min
//A = 0 if x >= min
A = 1-(min-min/x)/min
//B = 0 if x <= max
//B = 1 if x > max
B = (max-max/x)/max
x = A*min + (1-A)*(1-B)*x + B*max
Upvotes: 3