web-dev-qa-db-ja.com

abs関数もifステートメントも使用せずに絶対値を取得します

ifステートメントもabs()も使用せずに整数の絶対値を取得する方法を考えていました。最初は左のシフトビットを使用していました(<<)、範囲外の負の符号を取得しようとしてから、ビットを元の位置に戻しますが、残念ながらうまくいきません。なぜ機能していないのか、それを行う他の方法を教えてください。

51
shanwu

Bit Twiddling Hacks から:

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;
43
Hasturkun
_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_が残ります。

24
perreal

枝なし*

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文ではないため、有効な回答でもあります。これは、コンパイラが論理的に分岐するコードのブランチフリーアセンブリコードを出力できないことを意味しません。

21
Sebastian Mach

32ビットの符号付き整数(Java)を想定すると、次のように記述できます。

_public static int abs(int x)
{
    return (x + (x >> 31)) ^ (x >> 31);
}
_

乗算も分岐もありません。

ところで、return (x ^ (x >> 31)) - (x >> 31);も同様に機能しますが、特許を取得しています。うん!

注:このコードは、条件ステートメント(10ビットVerison)の10倍以上かかる場合があります。これは、ハードウェアプログラミングシステムCなどに役立ちます。

8
flanglet

私はこのコードを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;
}
2
dewang

あなたが考える方法で符号付き整数をビットシフトすることは未定義の動作であり、したがってオプションではありません。代わりに、これを行うことができます:

int abs(int n) { return n > 0 ? n : -n; }

ifステートメントはなく、条件式のみです。

2
Kerrek SB

これを見ませんでした。 2の補数表現と32ビット整数の場合

( n >> 31 | 1 ) * n
2
MAG

以下を試してください:

int abs(int n) 
{
  return sqrt(n*n);
}
2
Jeremy

あなたの言語がboolのintキャストを許可している場合(C/C++など):

float absB(float n) {
    return n - n * 2.0f * ( n < 0.0f );
}
1

分岐または乗算なし:

int abs(int n) {
    int mask = n >> 31;
    return (mask & -n) | (~mask & n);
}
0
TurplePurtle

これはどうですか:

#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.
}
0
Antonin GAVREL

三項演算子を使用します。

y = condition ? value_if_true : value_if_false;
0

符号ビットを左にシフトアウトし、右にシフトして戻すには複数の理由があります(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;
0
chqrlie

どのようにそのことについて:

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

私はそれが少し遅れていることを知っていますが、ちょうど同じ問題を抱えていて、ここに着陸しました、これが役立つことを願っています。

0
Martin