if
ステートメントもabs()
も使用せずに整数の絶対値を取得する方法を考えていました。最初は左のシフトビットを使用していました(<<
)、範囲外の負の符号を取得しようとしてから、ビットを元の位置に戻しますが、残念ながらうまくいきません。なぜ機能していないのか、それを行う他の方法を教えてください。
int v; // we want to find the absolute value of v
unsigned int r; // the result goes here
int const mask = v >> sizeof(int) * CHAR_BIT - 1;
r = (v + mask) ^ mask;
_int abs(int v)
{
return v * ( (v<0) * (-1) + (v>0));
// simpler: v * ((v>0) - (v<0)) thanks Jens
}
_
このコードは、v
の値に_-1
_または_1
_を乗算してabs(v)を取得します。したがって、括弧内は_-1
_または_1
_のいずれかになります。
v
が正の場合、式_(v>0)
_は真であり、値_1
_を持ちますが、_(v<0)
_は偽(偽の場合は値0)になります。したがって、v
が正の場合_((v>0) - (v<0)) = (1-0) = 1
_。そして、式全体はv * (1) == v
です。
v
が負の場合、式_(v>0)
_はfalseであり、値_0
_になりますが、_(v<0)
_はtrue(値1)になります。したがって、負のv
の場合、_((v>0) - (v<0)) = (0-1) = -1
_。そして、式全体はv * (-1) == -v
です。
_v == 0
_の場合、_(v<0)
_と_(v>0)
_の両方が0に評価され、_v * 0 == 0
_が残ります。
枝なし*:
int abs (int n) {
const int ret[2] = { n, -n };
return ret [n<0];
}
注4.7積分変換/ 4:[...] If the source type is bool, the value false is converted to zero and the value true is converted to one.
*:コードに条件分岐がないという意味で。内部では、三項演算子も分岐を生成します。ただし、3項はif文ではないため、有効な回答でもあります。これは、コンパイラが論理的に分岐するコードのブランチフリーアセンブリコードを出力できないことを意味しません。
32ビットの符号付き整数(Java)を想定すると、次のように記述できます。
_public static int abs(int x)
{
return (x + (x >> 31)) ^ (x >> 31);
}
_
乗算も分岐もありません。
ところで、return (x ^ (x >> 31)) - (x >> 31);
も同様に機能しますが、特許を取得しています。うん!
注:このコードは、条件ステートメント(10ビットVerison)の10倍以上かかる場合があります。これは、ハードウェアプログラミングシステムCなどに役立ちます。
私はこのコードをCで試しましたが、動作します。
int abs(int n){
return n*((2*n+1)%2);
}
この回答が役に立てば幸いです。
ここにabs()
のない別のアプローチがあります。if/論理式/条件式もありません:ここでintが32ビット整数であると仮定します。アイデアは非常に単純です:(1 - 2 * sign_bit)
はsign_bit = 1 / 0 to -1 / 1
を変換します。
unsigned int abs_by_pure_math( int a ) {
return (1 - (((a >> 31) & 0x1) << 1)) * a;
}
あなたが考える方法で符号付き整数をビットシフトすることは未定義の動作であり、したがってオプションではありません。代わりに、これを行うことができます:
int abs(int n) { return n > 0 ? n : -n; }
if
ステートメントはなく、条件式のみです。
これを見ませんでした。 2の補数表現と32ビット整数の場合
( n >> 31 | 1 ) * n
以下を試してください:
int abs(int n)
{
return sqrt(n*n);
}
あなたの言語がboolのintキャストを許可している場合(C/C++など):
float absB(float n) {
return n - n * 2.0f * ( n < 0.0f );
}
分岐または乗算なし:
int abs(int n) {
int mask = n >> 31;
return (mask & -n) | (~mask & n);
}
これはどうですか:
#include <climits>
long abs (int n) { // we use long to avoid issues with INT MIN value as there is no positive equivalents.
const long ret[2] = {n, -n};
return ret[n >> (sizeof(int) * CHAR_BIT - 1)]; // we use the most significant bit to get the right index.
}
三項演算子を使用します。
y = condition ? value_if_true : value_if_false;
符号ビットを左にシフトアウトし、右にシフトして戻すには複数の理由があります(v << 1 >> 1
):
unsigned
にキャストすると、望ましい効果が得られます:(unsigned)v << 1 >> 1
は、パディングビットがない場合、符号ビットを取り除きますが、結果の値は、現在ではほとんどありませんが、符号+大きさの表現を持つシステムでのみv
の絶対値です。ユビキタス2の補数アーキテクチャでは、負のv
の結果の値はINT_MAX+1-v
Hasturkunのソリューションには、残念ながら実装定義の動作があります。
以下は、符号付き値の2の補数表現を持つシステム用に完全に定義されたバリエーションです。
int v; // we want to find the absolute value of v
unsigned int r; // the result goes here
unsigned int mask = -((unsigned int)v >> (sizeof(unsigned int) * CHAR_BIT - 1));
r = ((unsigned int)v + mask) ^ mask;
どのようにそのことについて:
value = value > 0 ? value: ~value + 1
これは、負の数は正の等価物の2の補数として格納され、最初に1の補数を作成して1を追加することで2の補数を作成できるという事実に基づいています
5 -> 0000 0101b
-5 -> (1111 1010b) + 1 -> 1111 1011b
基本的にこれを逆にすることでした
-5 -> 1111 1011b
5 -> (0000 0100b) + 1 -> 0000 0101b
私はそれが少し遅れていることを知っていますが、ちょうど同じ問題を抱えていて、ここに着陸しました、これが役立つことを願っています。