web-dev-qa-db-ja.com

Cのビット演算子を使用して、数値がゼロでないかどうかを確認します

_!_以外の有効な演算子を使用して、数値xがゼロ以外かどうかを確認します。

例:isNonZero(3) = 1isNonZero(0) = 0

法務:_~_ _&_ _^_ _|_ _+_ _<<_ _>>_

  • 注:ビット演算子のみを使用してください。 ifelseforなどは使用できません。
  • Edit1:演算子の数は10を超えてはなりません。
  • Edit2:intのサイズを4バイトと見なします。
_int isNonZero(int x) {
return ???;
}
_

_!_を使用するのは簡単ですが、_!_を使用せずにそれを行うにはどうすればよいですか?

25
Eternal Learner

Adamk関数の対数バージョン:

int isNotZero(unsigned int n){
  n |= n >> 16;
  n |= n >> 8;
  n |= n >> 4;
  n |= n >> 2;
  n |= n >> 1;
  return n & 1;
};

そして最速のものですが、アセンブリでは:

xor eax, eax
sub eax, n  // carry would be set if the number was not 0
xor eax, eax
adc eax, 0  // eax was 0, and if we had carry, it will became 1

アセンブリバージョンに似たものをCで作成できます。符号ビットといくつかの違いを試してみます。

編集:ここに私がCで考えることができる最も速いバージョンがあります:

1)負の数の場合:符号ビットが設定されている場合、数は0ではありません。

2)正の場合:0 - nは負であり、ケース1のようにチェックできます。合法的な操作のリストに-が表示されないため、代わりに~n + 1を使用します。

私たちが得るもの:

int isNotZero(unsigned int n){ // unsigned is safer for bit operations
   return ((n | (~n + 1)) >> 31) & 1;
}
41
ruslik
int isNonZero(unsigned x) {
    return ~( ~x & ( x + ~0 ) ) >> 31;
}

Intが32ビットであると仮定します(/ *編集:パラメータタイプを符号なし* /に変更したため、この部分は適用されなくなり、符号付きシフトは符号なしシフトとまったく同じように動作します)。

11
usta

なぜ物事を複雑にするのですか?

int isNonZero(int x) {
    return x;
}

IsNonZeroは正当なintを返すため、Cの規則ではゼロ以外のすべての値がtrueを意味するため、これが機能します。

例に示すように、isNonZero()関数は入力3に対して1を返す必要があると主張する人もいます。

C++を使用している場合でも、以前と同じくらい簡単です。

int isNonZero(int x) {
    return (bool)x;
}

3を指定すると、関数は1を返します。

OK、適切なブール型が欠落しているCでは機能しません。

次に、intが32ビットで+が許可されていると仮定します。

int isNonZero(int x) {
    return ((x|(x+0x7FFFFFFF))>>31)&1;
}

一部のアーキテクチャでは、最後の&1、xを符号なし(ランタイムコストがnull)にキャストするだけですが、これは未定義の動作であるため、実装に依存します(ターゲットアーキテクチャが符号付きシフトまたは論理シフト右を使用するかどうかによって異なります)。

int isNonZero(int x) {
    return ((unsigned)(x|(x+0x7FFFFFFF)))>>31;
}
10
kriss
int is_32bit_zero( int x ) {
    return 1 ^ (unsigned) ( x + ~0 & ~x ) >> 31;
}
  1. 減算1.(~0は、2の補数マシンでマイナス1を生成します。これは仮定です。)
  2. 1に反転した反転ビットのみを選択します。
  3. 最上位ビットは、xがゼロの場合に1を減算した結果として反転するだけです。
  4. 最上位ビットを最下位ビットに移動します。

オペレーターは6人です。 0xFFFFFFFF 5。 unsignedへのキャストは、2の補数マシンを考慮しません; v)。

http://ideone.com/Omobw

2
Potatoswatter

ビット単位OR数値のすべてのビット:

int isByteNonZero(int x) {
    return ((x >> 7) & 1) |
           ((x >> 6) & 1) |
           ((x >> 5) & 1) |
           ((x >> 4) & 1) |
           ((x >> 3) & 1) |
           ((x >> 2) & 1) |
           ((x >> 1) & 1) |
           ((x >> 0) & 1);
}

int isNonZero(int x) {
  return isByteNonZero( x >> 24 & 0xff ) |
         isByteNonZero( x >> 16 & 0xff ) |
         isByteNonZero( x >> 8  & 0xff ) |
         isByteNonZero( x       & 0xff );
}
1
adamk

基本的に、またはビットが必要です。たとえば、数値が8ビット幅であることがわかっている場合:

int isNonZero(uint8_t x)
{
    int res = 0;
    res |= (x >> 0) & 1;
    res |= (x >> 1) & 1;
    res |= (x >> 2) & 1;
    res |= (x >> 3) & 1;
    res |= (x >> 4) & 1;
    res |= (x >> 5) & 1;
    res |= (x >> 6) & 1;
    res |= (x >> 7) & 1;

    return res;
}
0
Nathan Fellman