1. ホーム
  2. c#

[解決済み] ビット演算の実用例 [終了しました]

2023-06-25 19:31:03

質問

  1. ビット演算は何に使ったことがありますか?
  2. なぜそんなに便利なのですか?
  3. 誰かとても簡単なチュートリアルを教えてください。

どのように解決するには?

誰もがフラグの使用例に夢中になっているようですが、ビット演算子の用途はそれだけではありません (おそらく最も一般的ですが)。また、C# は十分に高レベルの言語なので、他のテクニックはおそらくほとんど使用されないでしょうが、それでも知っておく価値はあります。以下は、私が思いつくものです。


<<>> もちろん、.NET JITオプティマイザがこれを実行してくれますが(他の言語のまともなコンパイラも同様)、もし本当に毎ミクロ秒を気にしているのなら、念のためこれを書いておくとよいでしょう。

これらの演算子のもうひとつの一般的な使用法は、2 つの 16 ビット整数を 1 つの 32 ビット整数に詰め込むことです。みたいな感じです。

int Result = (shortIntA << 16 ) | shortIntB;

これはWin32関数との直接のインタフェースの場合によくあることで、レガシーな理由でこのトリックを使うことがあります。

そしてもちろん、これらの演算子は、宿題の答え合わせをするときなど、経験の浅い人を混乱させたいときに便利です :)

実際のコードでは、代わりに乗算を使う方がはるかによいでしょう。 shlshr 命令を使用するため、パフォーマンス上のペナルティはありません。


非常に多くの奇妙なトリックが ^ 演算子 (XOR) を扱います。これは実際には非常に強力な演算子で、次のような性質があるからです。

  • A^B == B^A
  • A^B^A == B
  • もしあなたが A^B がわかっていれば AB がありますが、どちらかが分かればもう一方は計算できます。
  • 演算子は乗算/除算/加算/減算のようなオーバーフローを起こしません。

この演算子を使ったトリックをいくつか見てきました。

中間変数なしで2つの整数変数をスワップする。

A = A^B // A is now XOR of A and B
B = A^B // B is now the original A
A = A^B // A is now the original B

項目ごとに1つの追加の変数を持つ2重リンクリストです。これはC#ではほとんど使われませんが、1バイト単位でカウントされる組み込みシステムの低レベルプログラミングでは便利かもしれません。

このアイデアは、最初の項目へのポインタ、最後の項目へのポインタ、そしてすべての項目に対する pointer_to_previous ^ pointer_to_next . この方法では、どちらの端からでもリストを走査することができ、しかもオーバーヘッドは従来のリンクリストの半分で済みます。以下は、トラバースするためのC++のコードです。

ItemStruct *CurrentItem = FirstItem, *PreviousItem=NULL;
while (  CurrentItem != NULL )
{
    // Work with CurrentItem->Data

    ItemStruct *NextItem = CurrentItem->XorPointers ^ PreviousItem;
    PreviousItem = CurrentItem;
    CurrentItem = NextItem;
}

最後から順にたどるには、一番最初の行を FirstItem から LastItem . これもメモリの節約になりますね。

もうひとつ、私が ^ 演算子を使うもうひとつの場面は、複合型の型に対して HashCode を計算しなければならないときです。みたいな感じ。

class Person
{
    string FirstName;
    string LastName;
    int Age;

    public int override GetHashCode()
    {
        return (FirstName == null ? 0 : FirstName.GetHashCode()) ^
            (LastName == null ? 0 : LastName.GetHashCode()) ^
            Age.GetHashCode();
    }
}