web-dev-qa-db-ja.com

ビット単位の演算子(シフト以外)は、基数10で数学的に意味がありますか?

wiki によると、シフトは2の累乗を計算するために使用できます。

Nによる左算術シフトは、2 ^ nを乗算することと同等です(値がオーバーフローしない場合)。一方、2の補数値のnによる右算術シフトは、2 ^ nで除算し、負の無限大に向かって丸めることと同等です。

私はいつも他のビット演算子(~|&^)基数10に適用すると、数学的に意味がありますか?それらがどのように機能するかは理解していますが、そのような演算の結果を使用して、小数の世界で役立つものを計算できますか?

31
serg
「うん、10進数は私が言っていることです」

その場合、はい、それらはバイナリの場合ほど有用ではありませんが、いくつかの方法で基数10に拡張できます。

1つのアイデアは、&|などは、個々の2進数に対して算術mod-2を実行することと同じであるということです。 abが1桁の2進数の場合、

 a&b = a * b(mod 2)
 a ^ b = a + b(mod 2)
 〜a = 1-a(mod 2)
 a | b =〜(〜a&〜b)= 1-(1-a)*(1-b)(mod 2)

基数10に相当するものは、(これらは整数ではなく、桁ごとに適用されることに注意してください)

 a&b = a * b(mod 10)
 a ^ b = a + b(mod 10)
 〜a = 9-a(mod 10)
 a | b =〜(〜a&〜b)= 9-(9-a)*(9-b)(mod 10)

最初の3つは、 [〜#〜] bcd [〜#〜]~a9の補数)を使用する回路を設計するときに役立ちます。 )、非グラフ電卓など。ただし、方程式を書くときは、*+ではなく&^を使用します。最初のものは明らかに いくつかの古い暗号 でも使用されています。

一時変数なしで2つの整数を交換する楽しいトリックは、ビット単位のXORを使用することです。

void swap(int &a, int &b) {
   a = a ^ b;
   b = b ^ a; //b now = a
   a = a ^ b; //knocks out the original a
}

XORは可換であるため、これは機能します。したがって、a ^ b ^ b = aです。

15
Rich

はい、他にも便利な操作がありますが、(明らかな理由で)2の累乗を含む操作に向けられる傾向があります。奇数/偶数のテスト、2の累乗のテスト、2の最も近い累乗への切り上げ/切り下げなど。

Hacker's Delight by Henry S.Warrenを参照してください。

6
Paul R

私が使用したすべての言語(確かに、ほぼ排他的にCおよびC導関数)では、ビット演算子は排他的に整数演算です(もちろん、演算をオーバーライドしない限り)。

can 10進数のビットをいじる間(結局のところ、それらには独自のビットがあります)、必ずしも整数のビットをいじるのと同じ結果が得られるとは限りません。 10進数のビットの説明については、 単精度 および 倍精度 を参照してください。 10進数をいじるビットの有利な使用法の例については、 高速逆平方根 を参照してください。

[〜#〜]編集[〜#〜]

整数の場合、ビット演算は常に意味があります。ビット演算は整数用に設計されています。

n << 1 == n * 2
n << 2 == n * 4
n << 3 == n * 8

n >> 1 == n / 2
n >> 2 == n / 4
n >> 3 == n / 8

n & 1 == {0, 1}       // Set containing 0 and 1
n & 2 == {0, 2}       // Set containing 0 and 2
n & 3 == {0, 1, 2, 3} // Set containing 0, 1, 2, and 3

n | 1 == {1, n, n+1}
n | 2 == {2, n, n+2}
n | 3 == {3, n, n+1, n+2, n+3}

等々。

6
Brian S

ビット単位の演算子だけを使用して対数を計算できます...

ビット演算を使用してn = 2 ** xの指数を見つける[nの基数2の対数]

3
eruciform

ブール演算の代わりにビット演算を使用できる場合があります。たとえば、次のコード:

 if((a <0)&&(b <0)
 {
何かをする
 {

Cでは、これは次のように置き換えることができます。

 if((a&b)<0)
 {
何かをする
 {

これは、整数の1ビットが符号ビットとして使用されるために機能します(1は負を示します)。 and演算(a&b)は無意味な数値になりますが、その符号はビット単位であり、数値の符号であるため、結果の符号をチェックすることはできます。

これは、パフォーマンスに役立つ場合とそうでない場合があります。 2つのブールテスト/ブランチを実行すると、多くのアーキテクチャとコンパイラで悪化します。最新のx86コンパイラは、通常の構文であっても、新しい命令の一部を使用して単一のブランチを生成できる可能性があります。

いつものように、パフォーマンスが向上する場合は...コードにコメントを付けます。つまり、「通常の」方法をコメントに入れて、同等であるが高速であると言います。

同様に、〜| ^は、すべての条件が(x <0)である場合と同様に使用できます。比較条件については、通常、減算を使用できます。

 if((a <b)|(b <c))
 {
} 

になります:

 if(((a-b)|(b-c))<0)
 {
} 

a-bは、aがbより小さい場合にのみ負になるためです。 max intの2倍以内、つまり算術オーバーフローが発生した場合、これに問題が発生する可能性があるため、注意してください。

これらは場合によっては有効な最適化ですが、それ以外の場合はまったく役に立ちません。そして、本当に醜いものにするために、浮動小数点数にも符号ビットがあります... ;-)

例:例として、a、b、cの順序に応じてアクションを実行するとします。ネストされたif/else構造を実行するか、次のようにすることができます。

 x =((a <b)<< 2)| ((b <c)<< 1)| (c <a); 
スイッチ(x):

私はこれを最大9つの条件のコードで使用し、上記の減算を追加のロジックとともに使用して、未満ではなく符号ビットを分離しました。同等の分岐よりも高速です。ただし、標準がtrueを1として指定するようにずっと前に更新されたため、減算と符号ビット抽出を行う必要がなくなりました。条件付き移動などを使用すると、実際の未満は最近非常に効率的になります。

1
phkahler