【问题标题】:Most efficient/elegant way to clip a number?剪辑数字的最有效/优雅的方法?
【发布时间】:2012-03-08 14:18:33
【问题描述】:

给定一个实数(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;
}

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

【问题讨论】:

  • "cap" 通常只指一个上限。你想要的词是“剪辑”。
  • 我怀疑效率,但您的解决方案确实不可读。为什么不直接定义某种“clamp”函数并使用它。
  • 另请阅读此相关问题:stackoverflow.com/questions/427477/…
  • 嗯做了一个搜索并错过了:/一定是因为我最初使用的术语是 cap,而不是 clip/clamp。谢谢

标签: c++ algorithm math logic


【解决方案1】:

那么无聊、陈旧、可读且最短的呢:

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

更新

比利·奥尼尔补充道:

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

【讨论】:

  • +1 - 足够好,我删除了我的答案。请注意,在 Windows 上,您可能必须定义 NOMINMAX,因为它们定义了冲突的最小和最大宏:(
  • 这属于标准。 C++1z,有人吗?
  • @underscore_d 感谢您的更新。我希望它得到批准。
  • 以下是正确的实现方式吗? pastebin.com/m3yVJCsx我在我正在使用的库中看到了这个,至少在这个“通用”版本中使用浮点数比较似乎是错误的?
【解决方案2】:

为什么要重写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);

【讨论】:

  • 标准答案:因为不是每个人都愿意/允许/身体上能够加入 Boost。但是,建议将此实现迁移到 stdlib,但不知道当前状态。
  • 看起来它将成为 C++17 中的标准:en.cppreference.com/w/cpp/algorithm/clamp
  • clamp 也是 Swift 标准库的一部分
  • @underscore_d 不用担心它是 C++17 的一部分。在此之前,您可以继续不断地重新发明轮子,避免使用经过同行评审的可移植代码。
  • @TrevorBoydSmith 我对 Boost 没有任何反对意见,也不需要你为它而做作的布道。也就是说,看起来我在评估人们不只是做你想让他们做的事情并使用 Boost 的通常原因时并没有错,而且令人惊讶的是,这并不是因为他们只是想通过“避免 [ing] 来惹恼你”使用经过同行评审的可移植代码”。我也不明白为什么你编辑这个现有的答案是完全不同的,当(A)你可以发布你自己的但也可以(B)其他人已经发布了,但是好的。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    更新:添加了 C++17 的 &lt;algorithm&gt; 标头 std::clamp(value, low, high)

    在较旧的 C++ 版本中,我很少会超越...

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

    ...或者,如果您发现保持从左到右的 lower、n 和 upper 顺序更具可读性...

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

    (使用&lt;=&gt;=&lt;&gt; 更快,因为当术语相等时,它会避免进一步比较)

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

    我说很少而且从来不只是因为有时更少的分支可以更快,但如果您或与您一起工作的其他人可能会找到那个神秘的代码,最好避免它,除非它在性能关键代码和分析显示中这很重要。

    【讨论】:

      【解决方案5】:

      不优雅、不安全、昂贵但没有分支:

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

      【讨论】:

      • 这是我最喜欢的,因为它完全是迟钝的;但 fabs() 可能包含分支,具体取决于您的库。要真正无分支,您可以将fabs(x) 替换为(x * (1 + (x &lt; 0) * -2)
      • 假设x &lt; 0 的评估确实是无分支的。如何从浮点表示中提取符号位 (msb),清楚地记录不可移植性? :-)
      【解决方案6】:

      最好的显然是

      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】:

        你可能喜欢三元运算符:

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

        【讨论】:

          【解决方案8】:
          n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));
          

          【讨论】:

            【解决方案9】:

            如果你想使用 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】:

              以下头文件应该适用于 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 */
              

              【讨论】:

              • 为什么 f*** MS 定义了自己的 MIN MAX,为什么 C++ 标准库中没有像钳位值这样的东西?
              • @DarioOO 回复有点晚,但 std::clamp() 从 C++17 开始可用
              • 是时候了! XD
              • 使用 #define NOMINMAX 而不是 #undefing 他们
              猜你喜欢
              • 2010-10-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-26
              • 2010-11-05
              • 1970-01-01
              • 2012-05-03
              相关资源
              最近更新 更多