_!
_以外の有効な演算子を使用して、数値x
がゼロ以外かどうかを確認します。
例:isNonZero(3) = 1
、isNonZero(0) = 0
法務:_~
_ _&
_ _^
_ _|
_ _+
_ _<<
_ _>>
_
if
、else
、for
などは使用できません。int
のサイズを4バイトと見なします。_int isNonZero(int x) {
return ???;
}
_
_!
_を使用するのは簡単ですが、_!
_を使用せずにそれを行うにはどうすればよいですか?
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;
}
int isNonZero(unsigned x) {
return ~( ~x & ( x + ~0 ) ) >> 31;
}
Intが32ビットであると仮定します(/ *編集:パラメータタイプを符号なし* /に変更したため、この部分は適用されなくなり、符号付きシフトは符号なしシフトとまったく同じように動作します)。
なぜ物事を複雑にするのですか?
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;
}
int is_32bit_zero( int x ) {
return 1 ^ (unsigned) ( x + ~0 & ~x ) >> 31;
}
~0
は、2の補数マシンでマイナス1を生成します。これは仮定です。)x
がゼロの場合に1を減算した結果として反転するだけです。オペレーターは6人です。 0xFFFFFFFF
5。 unsigned
へのキャストは、2の補数マシンを考慮しません; v)。
ビット単位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 );
}
基本的に、またはビットが必要です。たとえば、数値が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;
}