実数(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;
}
私はこれを行うためのより良い方法が他にあると確信しています。だから私はこれをそこに置いています。
退屈で、古くて、読みやすく、まだ最短のものはどうですか:
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を定義する必要がある場合があることに注意してください。
既にあなたのために書かれている の何かを書き換えるのはなぜですか?
#include <boost/algorithm/clamp.hpp>
boost::algorithm::clamp(n, lower, upper);
C++ 17以降、これは STLの一部 になりました。
#include <algorithm>
std::clamp(n, lower, upper);
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 );
[〜#〜] 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などが保存されているかどうかをチェックしたいでしょう...
分岐が少ないほど速くなることがあるという理由だけで、決してそうではないということはめったにありません。
三項演算子が好きかもしれません:
value = value<lower?lower:value;
value = value>upper?upper:value;
優雅で、安全ではなく、高価ですが、ブランチレス:
n= 0.5 * (n + lower + fabs(n - lower));
n= 0.5 * (n + upper - fabs(upper - n));
最高は明らかに
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
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}です
n = n + ((n < lower) * (lower - n)) + ((n > upper) * (upper - n));
入力タイプの可能な値にクランプする、このようなことをしようとしている人への警告。
_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
_
試してみてください。問題は、比較が行われる前に、関数へのエントリ時にinput
がT
にキャストされることです。 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);
}
_
パフォーマンスが本当に重要な場合、不要な場合に割り当てを回避するインラインソリューションについてはどうでしょうか。
#define clip(n, lower, upper) if (n < lower) n= lower; else if (n > upper) n= upper
次のヘッダーファイルは、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 */