Ограничить значение между min и max, используя только арифметику

Можно ли ограничить значение в данном диапазоне между минимальным и максимальным, используя только арифметику? То есть, +-x/ а также %?

Я не могу использовать такие функции, как min, max ни IF-заявления.

Давайте предположим, что у меня есть диапазон [1850, 1880]для любых значений < 1850должно отображаться 1850, Для ценностей > 1880, 1880 должен отображаться. Было бы также приемлемо, если только 1850 был отображен за пределами диапазона.

Я старался:

x = (((x - xmax) % (xmax - xmin)) + (xmax - xmin)) % (xmax - xmin) + xmin 

но это дает разные значения в середине диапазона для значений ниже, чем xmin,

2 ответа

Решение

Я нашел это, пока возился в... Excel. Это работает только для строго положительных целых чисел. Хотя это не является более ограничительным, как ответ meowgoesthedog, потому что он также эффективно делит пополам целое пространство путем деления на два в конце. Он не использует мод.

//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

Если вы знаете размер целочисленного типа, вы можете извлечь его знаковый бит (при условии дополнения до двух), используя целочисленное деление:

// 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);
}

Возвращает 1, если s < 0 и 0 в противном случае; его можно использовать для расчета абсолютного значения:

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;
}

Желаемая функция выглядит так:

введите описание изображения здесь

Полезно сначала сдвинуть минимум к нулю:

введите описание изображения здесь

Эта форма функции может быть вычислена как сумма двух смещенных функций абсолютного значения ниже:

введите описание изображения здесь

Однако результирующая функция масштабируется в 2 раза; здесь помогает смещение на ноль, потому что нам нужно только разделить на 2 и вернуться к исходному минимуму:

// 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;
}

Это решение, хотя и удовлетворяет требованиям проблемы, ограничено целочисленными типами со знаком (и языками, которые допускают целочисленное переполнение - я не уверен, как это можно преодолеть, например, в Java).

Я нашел это решение в Python:

A = -1  # Minimum value
B = +1  # Maximum value
x = min(max(x, A), B)
Другие вопросы по тегам