負の数の場合は-1、正の数の場合は+1を返す関数が必要です。 http://en.wikipedia.org/wiki/Sign_function 私自身で書くのは簡単ですが、それはあるべきもののようです。どこかの標準ライブラリ.
編集:具体的には、私はフロートで動作する機能を探していました。
誰もまだブランチレスでタイプセーフなC++バージョンを投稿していません。
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
利点:
copysign
は遅くなります。特に昇格してから絞り込む必要がある場合はなおさらです。これはブランチレスで、非常に最適化されています警告:
符号なしの型に対してインスタンス化されると、チェックの< 0
部分はGCCの-Wtype-limits
警告を引き起こします。いくつかのオーバーロードを使うことでこれを避けることができます。
template <typename T> inline constexpr
int signum(T x, std::false_type is_signed) {
return T(0) < x;
}
template <typename T> inline constexpr
int signum(T x, std::true_type is_signed) {
return (T(0) < x) - (x < T(0));
}
template <typename T> inline constexpr
int signum(T x) {
return signum(x, std::is_signed<T>());
}
(これは最初の警告の良い例です。)
標準的な機能はわかりません。これを書くための面白い方法はここにあります:
(x > 0) - (x < 0)
これを行うためのもっと読みやすい方法は次のとおりです。
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
あなたが三項演算子が好きなら、あなたはこれをすることができます:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
一方の引数から符号を取り、もう一方の引数から絶対値を取る、copysign()と呼ばれるC99数学ライブラリ関数があります。
result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double
値の符号に応じて、+/ - 1.0の結果が得られます。浮動小数点ゼロに符号が付けられていることに注意してください。(+0)は+1になり、(-0)は-1になります。
どうやら、元のポスターの質問に対する答えはノーです。標準的な標準C++のsgn
関数はありません。
ほとんどの回答は元の質問を逃したようです。
C/C++に標準の符号関数(signum、sgn)はありますか?
標準ライブラリにはありませんが、copysign(1.0, arg)
とほぼ同じように使用できる copysign
があり、 boost
には真の符号関数があります。これも標準の一部かもしれません。
#include <boost/math/special_functions/sign.hpp>
//Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
template <class T>
inline int sign (const T& z);
最も高い評価のものを含む上記の解決策よりも速いです:
(x < 0) ? -1 : (x > 0)
C/C++に標準の符号関数(signum、sgn)はありますか?
はい、定義次第です。
C99以降の<math.h>
にsignbit()
マクロがあります。
int signbit
(real-floatingx
);signbit
マクロは、引数値の符号が負の場合に限り、ゼロ以外の値を返します。 C11§7.12.3.6
それでもOPは少し違うものを望んでいます。
負の数の場合は-1、正の数の場合は+1を返す関数が必要です。 ...フロートを扱う機能.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
より深く:
投稿は、x = 0.0, -0.0, +NaN, -NaN
の場合には特定されません。
古典的な signum()
は、+1
にx>0
、-1
にx>0
、0
にx==0
を返します。
多くの答えがすでにそれをカバーしていますが、x = -0.0, +NaN, -NaN
を扱っていません。多くの場合、整数ではない数字( NaN )や が通常使用されます。 -0.0 .
典型的な答えはsignnum_typical()
のように機能します-0.0, +NaN, -NaN
では、それらは0.0, 0.0, 0.0
を返します。
int signnum_typical(double x) {
if (x > 0.0) return 1;
if (x < 0.0) return -1;
return 0;
}
代わりに、この機能を提案してください。-0.0, +NaN, -NaN
では、-0.0, +NaN, -NaN
を返します。
double signnum_c(double x) {
if (x > 0.0) return 1.0;
if (x < 0.0) return -1.0;
return x;
}
分岐せずにそれを行う方法がありますが、それは非常にきれいではありません。
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
そのページには、他にも面白くて賢いものがたくさんあります。
符号をテストするだけでよい場合は、 signbit を使用します(引数に負の符号がある場合はtrueを返します)。特に-1または+1を返したい理由がわからない。 copysignの方が便利ですが、一部のプラットフォームでは負のゼロを部分的にしかサポートしていないため、signbitがおそらくtrueを返すような場合に、負のゼロに+1を返すように思われます。
一般に、C/C++には標準のsignum関数はなく、そのような基本的な関数がないことから、これらの言語について多くのことがわかります。
それとは別に、そのような関数を定義するための正しいアプローチについての両方の大多数の見解はある意味で正しいと考えています。2つの重要な警告を考慮に入れると、それに関する「論争」は実際には議論の余地がありません。
signum関数は、abs()
関数と同様に、常にそのオペランドの型を返す必要があります。なぜなら、signumは通常、絶対値が何らかの形で処理された後の絶対値との乗算に使用されます。したがって、signumの主な使用例は比較ではなく算術演算であり、後者には高価な整数から浮動小数点への変換は含まれません。コンバージョン.
浮動小数点型は単一の正確なゼロ値を特徴としていません:+0.0は "ゼロより上の無限小"として、-0.0は "ゼロ未満の無限小"として解釈することができます。これが、ゼロを含む比較が両方の値に対して内部的にチェックしなければならない理由であり、x == 0.0
のような式は危険な場合があります。
Cに関して言えば、整数型を使う上で最善の方法は、(x > 0) - (x < 0)
式を使うことだと思います。なぜなら、それはブランチフリーな方法で翻訳されるべきで、3つの基本操作しか必要としないからです。引数の型と一致する戻り型を強制するインライン関数を定義し、これらの関数を共通の名前にマップするためのC11 define _Generic
を追加することをお勧めします。
浮動小数点値では、C11のcopysignf(1.0f, x)
、copysign(1.0, x)
、およびcopysignl(1.0l, x)
に基づくインライン関数は、それらがブランチフリーである可能性が非常に高く、さらに整数からの結果をにキャストバックする必要がないという理由で進むべき道だと思います浮動小数点値signumの浮動小数点実装は、浮動小数点ゼロ値の特殊性、処理時間の考慮事項、および浮動小数点演算では、ゼロの値に対しても正しい-1/+ 1符号を受け取ることが非常に便利であるためです。
私の一言で言えばCは、copysignという標準関数が存在することを示しています。 copysign(1.0、-2.0)は-1.0を返し、copysign(1.0、2.0)は+1.0を返すように見えます。
かなり近いでしょ?
下記の過負荷で受け入れられた答えは確かに - Wtype-limitsを引き起こさない。
template <typename T> inline constexpr
int signum(T x, std::false_type) {
return T(0) < x;
}
template <typename T> inline constexpr
int signum(T x, std::true_type) {
return (T(0) < x) - (x < T(0));
}
template <typename T> inline constexpr
int signum(T x) {
return signum(x, std::is_signed<T>());
}
C++ 11の場合、代替方法があります。
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T x) {
return T(0) < x;
}
template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T x) {
return (T(0) < x) - (x < T(0));
}
私にとっては、GCC 5.3.1に関する警告は表示されません。
いいえ、それはmatlabのようにc ++には存在しません。これにはプログラムでマクロを使用します。
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
Boostが利用可能であれば、boost/math/special_functions/sign.hpp
からboost::math::sign()
メソッドを使うことができます。
ちょっと話題にはなりませんが、私はこれを使用します。
template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
return (a > b) - (a < b);
}
template<typename T>
constexpr int sgn(const T &a) noexcept{
return sgn(a, T(0));
}
そして私は最初の関数 - 二つの引数を持つもの - が "標準的な" sgn()からもっと有用であることを見出しました。
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
vs.
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
符号なし型へのキャストや追加のマイナスはありません。
実際、私はsgn()を使用してこのコードを持っています
template <class T>
int comp(const T &a, const T &b){
log__("all");
if (a < b)
return -1;
if (a > b)
return +1;
return 0;
}
inline int comp(int const a, int const b){
log__("int");
return a - b;
}
inline int comp(long int const a, long int const b){
log__("long");
return sgn(a, b);
}
これは分岐にやさしい実装です。
inline int signum(const double x) {
if(x == 0) return 0;
return (1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
あなたのデータが数の半分としてゼロを持たない限り、ここで分岐予測子は最も一般的なものとして分岐の1つを選びます。どちらの分岐も単純な操作のみを含みます。
あるいは、一部のコンパイラやCPUアーキテクチャでは、完全にブランチのないバージョンが速いかもしれません。
inline int signum(const double x) {
return (x != 0) *
(1 - (static_cast<int>((*reinterpret_cast<const uint64_t*>(&x)) >> 63) << 1));
}
これは、 IEEE 754倍精度2進浮動小数点形式、binary64 で機能します。
受け入れられた答えの整数解は非常に洗練されていますが、double型に対してNANを返すことができないことを私に悩ませたので、私はそれをわずかに修正しました。
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
ハードコードされたNAN
ではなく浮動小数点NANを返すと、符号ビットが いくつかの実装 に設定されるので、val = -NAN
とval = NAN
の出力何に関係なく同一になるでしょう(-nan
よりも "nan
"の出力を好む場合は、リターンの前にabs(val)
を置くことができます...)
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
この関数は、次のことを前提としています。