剪辑数字最有效/优雅的方式?

时间:2012-02-17 06:32:58

标签: c++ algorithm math logic

给定一个实数(n),这个实数的最大值可以是(上限),这个实数的最小值可以是(更低),我们怎样才能最有效地剪切n,使它保持在低位和高位之间?

当然,使用一堆if语句可以做到这一点,但那很无聊!那些更紧凑,更优雅/更有趣的解决方案呢?

我自己的快速尝试(C / C ++):

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

我确信还有其他更好的方法可以做到这一点,这就是我把它放在那里的原因..!

13 个答案:

答案 0 :(得分:75)

枯燥,陈旧,可读和最短的东西:

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

这个表达式也可以像这样“泛化”:

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

<强>更新

Billy ONeal补充道:

  

请注意,在Windows上,您可能必须定义NOMINMAX,因为它们定义了冲突的最小和最大宏

答案 1 :(得分:35)

为什么要重写一下already been written for you

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

从C ++ 17开始,现在是part of the STL

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

答案 2 :(得分:19)

C ++ 17应该添加clamp函数。由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 );

答案 3 :(得分:15)

我很少超越......

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

如果你知道你可能有它们,你想检查NaN / Inf等是否被保留....

我很少说,也不仅仅是因为有时候较少的分支可以更快,但你肯定想要描述它并证明它有帮助和重要....

答案 4 :(得分:5)

您可能喜欢三元运算符:

value = value<lower?lower:value;
value = value>upper?upper:value;

答案 5 :(得分:4)

不雅,不安全,昂贵但无分支:

n= 0.5 * (n + lower + fabs(n - lower));
n= 0.5 * (n + upper - fabs(upper - n));

答案 6 :(得分:2)

最好的显然是

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

编译为

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

它有0个分支,应该是上面发布的最快的分支。

还带有标准版本设置的msvc141

答案 7 :(得分:1)

n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));

答案 8 :(得分:1)

双曲正切函数以非常优雅的方式完成(对神经网络使用很多)。请参阅下面的代码。

Tanh function axed on 0

.hex-bg {
    background-image: url('/images/diamond.png');
    background-size: cover;
    background-repeat: no-repeat;
    background-position:center;
    min-height: 30px;
    box-sizing:border-box;
    padding: 20px;
    width:250px;
    height:290px;
    text-align:center;
    color:white;
    font-size:14px;
}

.hex-bg img{
    max-width:50px;
    padding:20px;
}

.text-wrapper{
    margin-top:10px;
}

答案 9 :(得分:1)

如果你想使用xtensor,它将支持多维数组,解决方案非常优雅。

#include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor/xview.hpp"
#include "xtensor/xrandom.hpp"
xt::xarray<float> ar({2.1, 2.9, -2.1, -2.9});
std::cout<<xt::cast<int>(xt::trunc(ar))<<std::endl;

//答案是{2,2,2,-2}

答案 10 :(得分:0)

以下头文件应该适用于C和C ++。请注意,如果宏已经定义,则未定义min和max:

#pragma once

#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif

#ifdef __cplusplus
#include <algorithm>

template <typename T>
T clip(T in, T low, T high)
{
    return std::min(std::max(in, low), high);
}
#else /* !__cplusplus */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) < (b)) ? (b) : (a))
#define clip(a, b, c) min(max((a), (b)), (c))
#endif /* __cplusplus */

答案 11 :(得分:0)

警告任何试图执行此类操作的人,在此情况下,您将锁定输入类型的可能值。

template<typename T>
T clamp(T input)
{
    return boost::algorithm::clamp(input, 
                                   std::numeric_limits<T>::min(),
                                   std::numeric_limits<T>::max());
}

这对于Tinput的某些值将失败。例如:

clamp<int16_t>(32768.0);

将返回

-32767

尝试一下,看看。问题在于,在进行比较之前,input在进入函数时被强制转换为T。如果您static_cast<int16_t>(+32768),您将得到UB。

除了下面的代码“更好”但不完整之外,我没有一个好的解决方案。这适用于较小的整数类型(int16_tint32_t)和单精度float,但对于int64_tdouble却有问题。

template<typename T>
T clamp(double input)
{
    double intermediate = return boost::algorithm::clamp<double>(input, 
                        std::numeric_limits<T>::min(),
                        std::numeric_limits<T>::max());
    return boost::numeric_cast<T>(intermediate);
}

答案 12 :(得分:-1)

如果性能对您来说真的很重要,那么内联解决方案可以避免在不需要时进行分配:

#define clip(n, lower, upper) if (n < lower) n= lower; else if (n > upper) n= upper