web-dev-qa-db-ja.com

数字をクリップする最も効率的でエレガントな方法は?

実数(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;
}

私はこれを行うためのより良い方法が他にあると確信しています。だから私はこれをそこに置いています。

44
Alex Z

退屈で、古くて、読みやすく、まだ最短のものはどうですか:

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では、競合するminおよびmaxマクロを定義するため、NOMINMAXを定義する必要がある場合があることに注意してください。

77
justin

既にあなたのために書かれている の何かを書き換えるのはなぜですか?

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

C++ 17以降、これは STLの一部 になりました。

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

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 );
19
Josh Kelley

[〜#〜] update [〜#〜]:C++ 17の _<algorithm>_ ヘッダーを追加- 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;
_

(_<= lower_を使用すると、_< lower_よりも優れています。なぜなら、_n == lower_の場合、upperと比較する必要がなくなるからです)

あなたがそれらを持っているかもしれないことを知っているなら、NaN/Infなどが保存されているかどうかをチェックしたいでしょう...

分岐が少ないほど速くなることがあるという理由だけで、決してそうではないということはめったにありません。

16
Tony Delroy

三項演算子が好きかもしれません:

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

優雅で、安全ではなく、高価ですが、ブランチレス:

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

最高は明らかに

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

2
nsn

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}です

1
Achyut Sarma

双曲線正接関数は、非常に洗練された方法でそれを行います(ニューラルネットワークで多く使用されます)。以下のコードを参照してください。

Tanh function axed on 0

float clip(float x, float min, float max) {
  return ((max-min)/2)*((exp(x) - exp(-x))/(exp(x) + exp(-x))) + max - (max-min)/2;
}
1
HichameMoriceau
n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));
1
user997112

入力タイプの可能な値にクランプする、このようなことをしようとしている人への警告。

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

Tおよびinputの一部の値では失敗します。例えば:

_clamp<int16_t>(32768.0);
_

戻ります

_-32767
_

試してみてください。問題は、比較が行われる前に、関数へのエントリ時にinputTにキャストされることです。 static_cast<int16_t>(+32768)を使用すると、UBが取得されます。

「より良い」が完全ではない以下のコード以外には、良い解決策はありません。これは、小さな整数型(_int16_t_および_int32_t_)および単精度floatで機能しますが、_int64_t_およびdoubleに問題があります。

_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);
}
_
1
Mark Lakata

パフォーマンスが本当に重要な場合、不要な場合に割り当てを回避するインラインソリューションについてはどうでしょうか。

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

次のヘッダーファイルは、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 */
0
Woody