Наиболее краткая реализация функции ограничения с плавающей запятой с циклическим переполнением - PullRequest
1 голос
/ 14 марта 2011

Я ищу наиболее краткую и общую реализацию следующей функции:

float Constrain(float value, float min, float max);

Где Constrain () ограничивает value в диапазоне [min, float). То есть диапазон включает мин, но исключает max и values больше чем max или меньше чем min, обернутые в круг То есть аналогично целым числам over / underflow.

Функция должна пройти следующие тесты:

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

Обратите внимание, что параметр min всегда можно считать численно меньшим, чем max.

Вероятно, есть очень простая формула для решения этого вопроса, но я невероятно туп, не зная его обобщенного решения.

Ответы [ 4 ]

2 голосов
/ 15 марта 2011

Вы почти ищете функцию fmod. fmod(x,y) возвращает остаток от деления x на y, оба значения равны double с. Знак результата такой же, как и у x (эквивалентно, соответствующая функция целочисленной части - это та, которая округляется до нуля), и поэтому это только почти , что вы хотите. Итак, если x>=lo, то lo+fmod(x-lo,hi-lo) - это правильная вещь, но если x<lo, тогда hi+fmod(x-lo,hi-lo) - это почти такая же правильная вещь, за исключением случаев, когда x<lo и результатом может быть либо lo, либо hi вы получите hi вместо lo.

Итак. Вы можете разделить тремя способами:

double Constrain(x,lo,hi) {
  double t = fmod(x-lo,hi-lo);
  return t<0 ? t+hi : t+lo;
}

или вместо этого вы можете использовать floor [РЕДАКТИРОВАНИЕ, потому что первая версия этого совсем не то, что я имел в виду]:

double Constrain(x,lo,hi) {
  double t = (x-lo) / (hi-lo);
  return lo + (hi-lo) * (t-floor(t));
}

Выберите, если вас волнует понятность; попробуйте оба варианта, если вас интересует производительность.

1 голос
/ 04 мая 2012

lrint () может быть быстрее.

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;
}
1 голос
/ 04 мая 2012

lrint () может быть быстрее.

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);
}
0 голосов
/ 18 октября 2016

Это также работает:

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