負の数に対して-1を返し、正の数に対して+1を返す関数が必要です。 http://en.wikipedia.org/wiki/Sign_function 自分で作成するのは簡単ですが、標準ライブラリのどこかにあるはずです。
編集:具体的には、フロートで機能する関数を探していました。
x==0
ます。IEEE 754によれば、負のゼロと正のゼロは等しいものとして比較する必要があります。
負の数に対して-1を返し、正の数に対して+1を返す関数が必要です。 http://en.wikipedia.org/wiki/Sign_function 自分で作成するのは簡単ですが、標準ライブラリのどこかにあるはずです。
編集:具体的には、フロートで機能する関数を探していました。
x==0
ます。IEEE 754によれば、負のゼロと正のゼロは等しいものとして比較する必要があります。
回答:
誰もまだタイプセーフなC ++バージョンを投稿していないことに驚いています。
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
利点:
copysign
特にプロモートしてから再び絞り込む必要がある場合は遅いです。これは分岐がなく、最適化します警告:
< 0
チェックの一部は、-Wtype-limits
署名されていない型に対してインスタンス化されたときにGCCの警告をトリガーします。これを回避するには、いくつかのオーバーロードを使用します。
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>());
}
(これは最初の警告の良い例です。)
std::copysign
になるように思われる優れた私のためのコード:4つの命令(インライン化)、分岐のない、完全にFPUを使用しました。対照的に、この回答で与えられたレシピは、はるかに悪いコードを生成します(乗算、整数ユニットとFPUの間を前後に移動するなど、より多くの命令)...
copysign
intを呼び出す場合、float / doubleに昇格し、戻り時に再び狭める必要があります。あなたのコンパイラはそのプロモーションを最適化するかもしれませんが、私はそれが標準によって保証されることを示唆するものを見つけることができません。また、copysignを介してsignumを実装するには、手動で0のケースを処理する必要があります。パフォーマンスの比較には必ずそれを含めてください。
そのための標準的な関数は知りません。ここにそれを書く興味深い方法があります:
(x > 0) - (x < 0)
これを行うためのより読みやすい方法を次に示します。
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
三項演算子が好きなら、これを行うことができます:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
x==0
。
<
、>
falseの場合は0を返します」
0
は「false」です。その他の値はすべて「true」です。ただし、関係演算子と等価演算子は常に0
or を返します1
(Standard 6.5.8および6.5.9を参照)。-式の値はまたa * (x == 42)
はのいずれ0
かa
です。
copysign
、積分には使用しませx
ん。
copysign()と呼ばれるC99数学ライブラリ関数があります。これは、1つの引数から符号を取り、もう1つの引数から絶対値をとります。
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 / C ++に標準の符号関数(signum、sgn)はありますか?
標準ライブラリにはありませんが、をcopysign
介してほぼ同じように使用できるものがありcopysign(1.0, arg)
、には真の符号関数があり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);
どうやら、元のポスターの質問への答えはノーです。標準の C ++ sgn
関数はありません。
copysign()
2番目が0.0の場合、最初のパラメータは0.0になりません。つまり、ジョンは正しいです。
C / C ++に標準の符号関数(signum、sgn)はありますか?
はい、定義によります。
C99以降にはsignbit()
マクロが<math.h>
int signbit
(リアルフローティングx
); マクロ返す引数値の符号が負である場合にのみ非ゼロ値。C11§7.12.3.6signbit
しかし、OPは少し違うものを求めています。
負の数に対して-1を返し、正の数に対して+1を返す関数が必要です。... floatを処理する関数。
#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()
Onのように機能し-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
そのページには、他にも興味深い、過度に賢いものがたくさんあります...
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
またはsign = (v > 0) - (v < 0);
です。
v
は、それがintより広くない整数型であることを意味します
一般に、C / C ++には標準のsignum関数はなく、そのような基本的な関数がないことから、これらの言語について多くのことがわかります。
それとは別に、そのような関数を定義するための適切なアプローチに関する両方の多数の見方はある程度正しいと思います。2つの重要な注意点を考慮すると、それに関する「論争」は実際には議論の余地はありません。
シグナム関数は常に、同様に、そのオペランドの型を返すべきであるabs()
ので、機能シグナムは、後者が何らかの形で処理された後、通常の絶対値との乗算のために使用されます。したがって、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)
彼らは可能性が高い枝がないことをもだし、さらに浮動小数点への整数の後ろから結果をキャストする必要がないという理由だけで、移動するための方法です値。おそらく、浮動小数点のゼロ値の特殊性、処理時間の考慮事項、および正しい-1 / +を受け取る浮動小数点演算で非常に役立つため、signumの浮動小数点実装がゼロを返さないことをはっきりとコメントする必要があります。ゼロ値の場合でも1符号。
NutshellでのCの私のコピーは、便利かもしれないcopysignと呼ばれる標準関数の存在を明らかにします。copysign(1.0、-2.0)は-1.0を返し、copysign(1.0、2.0)は+1.0を返すように見えます。
かなり近いですか?
いいえ、MATLABのようにC ++には存在しません。このためにプログラムでマクロを使用しています。
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
#define sign(x) (((x) > 0) - ((x) < 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 const x) {
return T(0) < x;
}
template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T const x) {
return (T(0) < x) - (x < T(0));
}
私にとっては、GCC 5.3.1で警告はトリガーされません。
-Wunused-parameter
警告を回避するには、名前のないパラメーターを使用します。
少し話題外ですが、私はこれを使用します:
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));
}
そして私は最初の関数を見つけました-2つの引数を持つ関数は、「標準」のsgn()からはるかに役立つためです。
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
対
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);
}
質問は古いですが、今はこの種の望ましい機能があります。not、left shift、decのラッパーを追加しました。
C99からのsignbitに基づくラッパー関数を使用して、正確な望ましい動作を得ることができます(以下のコードを参照)。
xの符号が負かどうかを返します。
これは、無限大、NaN、およびゼロにも適用できます(ゼロが符号なしの場合、正と見なされます)
#include <math.h>
int signValue(float a) {
return ((!signbit(a)) << 1) - 1;
}
注:signbitの戻り値が1に指定されていないため(例では常にこのようになると考えています)、オペランドnot( "!")を使用していますが、負の数の場合はtrueです。
戻り値
xの符号が負の場合、ゼロ以外の値(true)。それ以外の場合はゼロ(false)。
次に、2を左シフト( "<< 1")で乗算します。これにより、正数の場合は2、負数の場合は0になり、最終的に1減分して、要求に応じて正数と負数のそれぞれ1と-1を取得します。 OP。
受け入れられた回答の整数解は非常に洗練されていますが、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)
リターンの前に...)
ブーストが利用可能な場合はboost::math::sign()
、からのメソッドを使用boost/math/special_functions/sign.hpp
できます。
これはブランチフレンドリーな実装です:
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));
}
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
この関数は、以下を前提としています。