Most succinct implementation of a floating point constraint function with wrap-around overflow

亡梦爱人 提交于 2020-01-03 02:46:09

问题


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.


回答1:


You're almost looking for the fmod function. fmod(x,y) returns the remainder on dividing x by y, both being doubles. 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.




回答2:


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



回答3:


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



回答4:


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


来源:https://stackoverflow.com/questions/5304423/most-succinct-implementation-of-a-floating-point-constraint-function-with-wrap-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!