Qliver
Qliver

Reputation: 61

Limit a value between min and max using only arithmetic

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

Answers (3)

I found this solution in Python:

A = -1  # Minimum value
B = +1  # Maximum value
x = min(max(x, A), B)

Upvotes: -1

meowgoesthedog
meowgoesthedog

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:

enter image description here

It is useful to first shift the minimum to zero:

enter image description here

This function form can be computed as a sum of the two shifted absolute value functions below:

enter image description here

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

JerMah
JerMah

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

Related Questions