Most efficient/elegant way to clip a number?

82,072

Solution 1

What about boring, old, readable, and shortest yet:

float clip(float n, float lower, float upper) {
  return std::max(lower, std::min(n, upper));
}

?

This expression could also be 'genericized' like so:

template <typename T>
T clip(const T& n, const T& lower, const T& upper) {
  return std::max(lower, std::min(n, upper));
}

Update

Billy ONeal added:

Note that on windows you might have to define NOMINMAX because they define min and max macros which conflict

Solution 2

Why rewrite something that's already been written for you?

#include <boost/algorithm/clamp.hpp>
boost::algorithm::clamp(n, lower, upper);

As of C++17, this is now part of the STL:

#include <algorithm>
std::clamp(n, lower, upper);

Solution 3

C++17 is expected to add a clamp function. Courtesy of cppreference.com:

template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi );

template<class T, class Compare>
constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp );

Solution 4

UPDATE: C++17's <algorithm> header added std::clamp(value, low, high).

In older C++ versions, I'd very rarely go beyond...

return n <= lower ? lower : n >= upper ? upper : n;

...or, if you find it more readable keeping the left-to-right ordering of lower, n and upper...

return n <= lower ? lower : n <= upper ? n : upper;
...or...
return lower >= n ? lower : n <= upper ? n : upper;

(using <=, >= is faster than <, > because when the terms are equal it avoids further comparisons)

If you know you might have them, you'd want to check if NaN / Inf etc. are preserved....

I say rarely and not never just because sometimes less branching can be faster, but if you or other people you work with are likely to find the code for that cryptic, it's best avoided unless it's in performance-critical code and profiling shows it matters.

Solution 5

the best is clearly

template <typename t>
t clamp2(t x, t min, t max)
{
if (x < min) x = min;
if (x > max) x = max;
return x;
}

as it compiles to

movss   xmm0, cs:__real@c2c80000
maxss   xmm0, [rsp+38h+var_18]
movss   xmm1, cs:__real@42c80000
minss   xmm1, xmm0
movss   [rsp+38h+var_18], xmm1

it has 0 branches and should be the fastest of all posted above.

also msvc141 with the standard release settings

Share:
82,072
Alex Z
Author by

Alex Z

Updated on July 09, 2022

Comments

  • Alex Z
    Alex Z almost 2 years

    Given a real (n), a maximum value this real can be (upper), and a minimum value this real can be (lower), how can we most efficiently clip n, such that it remains between lower and upper?

    Of course, using a bunch of if statements can do this, but that's boring! What about more compact and elegant/fun solutions?

    My own quick attempt (C/C++):

    float clip( float n, float lower, float upper )
    {
        n = ( n > lower ) * n + !( n > lower ) * lower;
        return ( n < upper ) * n + !( n < upper ) * upper;
    }
    

    I'm sure there are other, better ways to do this, that's why I'm putting this out there..!