ビット演算子は何に適していますか?[閉まっている]


19

プログラミング言語には、多くの場合、さまざまなビット演算子が付属しています(ビット単位の左シフトおよび右シフト、ビット単位のAND、OR、XORなど)。これらはあまり使用されませんが、少なくとも私の経験ではそうです。これらは、プログラミングの課題やインタビューの質問で使用される場合があります。または、ソリューションで必要になる場合があります。たとえば、

  • 等値演算子を使用せずにtrue、2つの値が等しいときに戻る関数を作成します
  • 3番目の変数を使用せずに、2つの変数の値を交換します

これらは再び、おそらく実際の使用はほとんどありません。低レベルでメモリを直接操作するため、より高速になるはずです。

なぜこれがほとんどのプログラミング言語で見つかるのですか?現実のユースケースはありますか?


@Anto-簡単な例は、256Kbのデータを一度に256ワード(4096バイト)のレートでクライアントに送信することです。
ラムハウンド

1
「等値演算子を使用せずに、2つの値が等しい場合にtrueを返す関数を作成します」-Cの場合:return !(x-y);?私
アンドリューアーノルド

@Andrew:それは解決策ですが、ビットごとの演算子でも同様にできます。
アント

19
「これらはあまり使われません」 -確かに?私は常にそれらを使用しています。問題のドメインですべてが機能するとは限りません。
エドS.

2
完全な答えを得るには十分ではありませんが、ビットをいじることなく、バイトの上位4ビットを読み取ってから、一部のデータ形式が非常に密集していることを考慮してください。
モニカを

回答:


53

いいえ、それらには多くの実世界のアプリケーションがあり、コンピューターの基本的な操作です。

彼らはに使用されます

  • プログラミング言語のデータ型に収まらないバイトブロックをジャグリングする
  • エンコーディングをビッグエンディアンからリトルエンディアンに切り替えます。
  • シリアル接続またはUSB接続のために、4つの6ビットのデータを3バイトにパック
  • 多くの画像形式では、各カラーチャンネルに割り当てられるビットの量が異なります。
  • 組み込みアプリケーションのIOピンに関係するもの
  • 多くの場合、データが適切な8ビット境界に適合しないデータ圧縮。
  • ハッシュアルゴリズム、CRCまたはその他のデータ整合性チェック。
  • 暗号化
  • 擬似乱数生成
  • RAID 5は、ボリューム間のビット単位のXORを使用してパリティを計算します。
  • トン以上

実際、論理的には、コンピューター上のすべての操作は最終的にこれらの低レベルのビット単位操作の組み合わせに要約され、プロセッサの電気ゲート内で行われます。


1
+1を追加すると思われる包括的なリストに追加
アント

28
+1。@Anto:このリストは包括的なものではありません。システムプログラミングにおけるビット演算子のユースケースの包括的なリストは、ビジネスアプリケーションのSQLクエリの包括的なリストと同じくらい長くなります。楽しい事実:私は常にビット演算を使用していますが、何年もSQLステートメントを書いていません
;

4
@nikie:そして、私は常にSQLを書いていますが、長年ビット単位の演算子を使用していません!:)
FrustratedWithFormsDesigner

3
私は組み込みシステムで働いています-ビット演算子はパンとバターです。まったく考えずに毎日使用します。
すぐに

9
SQLでビットシフトをときどき使用する場合、賞品はもらえますか?
アリ

13

基本的な操作だからです。

同じ考え方で、加算は減算(および否定)と乗算で完全に置き換えることができるため、加算には実際の使用がほとんどないと主張できます。しかし、基本的な操作であるため、追加し続けます。

また、ビット操作の必要性があまり見られないからといって、それらがあまり頻繁に使用されないわけではないとは考えないでください。実際、ビットマスキングなどに使用したほぼすべての言語でビット演算を使用しました。

私は、画像処理、ビットフィールドとフラグ、テキスト処理(たとえば、特定のクラスのすべての文字が共通のビットパターンを共有することが多い)、シリアル化されたデータのエンコードとデコード、VMまたはCPUのデコードにビット演算を使用しましたオペコードなど。ビット単位の操作がなければ、これらのタスクのほとんどは、タスクの信頼性を低くしたり、読みにくくしたりするために、何倍も複雑な操作を必要とします。

例えば:

// Given a 30-bit RGB color value as a 32-bit int
// A lot of image sensors spit out 10- or 12-bit data
// and some LVDS panels have a 10- or 12-bit format
b = (color & 0x000003ff);
g = (color & 0x000ffc00) >> 10;
r = (color & 0x3ff00000) >> 20;

// Going the other way:
color = ((r << 20) & 0x3ff00000) | ((g << 10) & 0x000ffc00) | (b & 0x000003ff);

RISCタイプのCPUのCPU命令をデコードするには(別のプラットフォームをエミュレートするときなど)、上記のように大きな値の部分を抽出する必要があります。乗算、除算、モジュロなどを使用してこれらの演算を実行すると、同等のビット演算の10倍も遅くなる場合があります。


12

典型的な例は、24ビットのRGB値から個々の色を抽出して戻すことです。


編集:http : //www.docjar.com/html/api/java/awt/Color.java.htmlから

    value =  ((int)(frgbvalue[2]*255 + 0.5))    |
                (((int)(frgbvalue[1]*255 + 0.5)) << 8 )  |
                (((int)(frgbvalue[0]*255 + 0.5)) << 16 ) |
                (((int)(falpha*255 + 0.5)) << 24 );

この例を実際に表示しますか?コードスニペット?
アント

より良い例は、16ビット(4.5bpc)または30ビット(10bpc)のRGB値を処理することです。
-greyfade

@grey、このような例を自由に追加してください。

6

以下に、Quake 3、Quake 4、Doom IIIにある実世界の例示します。Q3エンジンを使用したすべてのゲーム

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

(浮動小数点数がどのように保存されるかを理解する必要があるコードを理解するために、私は間違いなくそれについて詳しく説明することはできません)

使用に関しては、ネットワーキングやグラフィックスなどのビットシフトを必要とする分野にいる場合を除き、その目的が少しアカデミックであることがわかります。しかし、まだ興味深い(少なくとも私にとって)。


+1それらのコメントについては、たとえそれらがあなたのものでなくても。私を笑わせた。
バシネーター

4

シフトは、2の累乗で乗算または除算するよりも高速です。たとえば、<< = 2はaを4で乗算します。逆に、>> = 2はaを4で除算します。ビット単位の演算子を使用して、データをデバイスにビットバングすることもできます。たとえば、Nループ内でシフト、xor、および「and」操作を使用して、NピンポートからN個のシリアルデータストリームを送信できます。デジタルロジックで実現できることはすべて、ソフトウェアでも実現でき、その逆も可能です。


1
切り上げまたは切り捨てなどで除算する場合は注意してください。シフトはそれを考慮しないため、除算を行う場合はコードで除算を使用し、コンパイラーがシフトと加算に最適化できるようにする方が実際には良い方法であることがわかりました私のために。
デミン

@Daemin:この手法を使用するとき、整数を使用しています。CおよびC ++の整数除算のデフォルトの動作は、ゼロへの切り捨てです。したがって、整数を2のべき乗で右にシフトすると、整数を2のべき乗で除算するのと同じ結果になります。
ビットトゥイドラ

1
@ bit-twiddler右シフトは、負の数の除算と同じようには機能しません。
デミン

@Daemin:あなたは私が間違っていることを証明することに夢中になっているようです。最初に、丸めの問題を投げます。CとC ++での除算がゼロに切り捨てられると述べることでその主張を否認すると、符号付き整数の問題は捨てられます。シフト演算子を符号付き2の補数の負の整数に適用すると言ったのはどこですか?それでも、シフト演算子を使用して2の累乗で割ることができます。ただし、CおよびC ++は単純な古い右シフトではなく算術右シフトを実行するため、値が負であるかどうかを最初に確認する必要があります。値が負の場合、
ビットツイダー

1
正確に、微妙な違いがあるので、乗算と除算の代替としてシフトを使用する場合は注意してください。これ以上でもそれ以下でもない。
デミン

3

ずっと昔、ビット演算子は便利でした。今日はそうではありません。ああ、それらは完全に役に立たないわけではありませんが、使用されるべきものが使用されているのを私が見たので、それは長い時間です。

1977年、私はアセンブリ言語プログラマーでした。アセンブラーが唯一の真の言語だと確信しました。パスカルのような言語は、実際に何もする必要のないアカデミックなウィニー向けであると確信しました。

それから、カーニハンとリッチーの「The C Programming Language」を読みました。それは私の心を完全に変えました。理由?ビット演算子がありました!それアセンブリ言語でした!構文が異なるだけです。

当時は、AND、OR、シフト、ローテーションなしでコードを書くことは考えられませんでした。最近ではほとんど使用しません。

したがって、あなたの質問に対する短い答えは「何もない」です。しかし、それはかなり公平ではありません。長い答えは「ほとんど何もありません」です。


xkcd.com/378が思い浮かびます。
Maxpm

今日、ビット演算子は便利です。ドメインで使用されていないという事実は、それを未使用にしたり、非常に頻繁に使用したりすることさえありません。次に簡単な例を示します。ビット演算子を使用せずにAESを実装してみてください。これは、ほとんどのコンピューターで1日に数百または数千回行われていることの1つの例です。
ちょうど私の正しい意見

ビット単位の演算子を使用せずにデータをエンコード/デコードすることは、せいぜい苦痛です。たとえば、MIME添付ファイルをメッセージに追加するには、データの3〜4のコーディング(radix64コーディングとも呼ばれる)を処理できる必要があります。
ビットツイダー

2

暗号化

DES暗号化アルゴリズムの非常に小さなスニペットをご覧になることをお勧めします

temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

DESが最近推奨されているかどうかはわかりませんが、P
アルマン

@Alison:いいえ。しかし、それに取って代わる暗号化アルゴリズムは、さらに多くのビット操作操作を伴うと思います。:-)
Carson63000

@Alison-もちろん、TripleDESは3回のキービットで3回実行されるDESにすぎません。
スコットホイットロック

2

良い答えがたくさんあるので、私はそれらの使用を繰り返しません。

私はそれらをマネージコード(C#/ .Net)で非常に多く使用していますが、スペース節約、高性能、または巧妙なビットシフトアルゴリズムとは何の関係もありません。この方法でデータを保存するのに適したロジックもあります。列挙型がある場合によく使用しますが、インスタンスはその列挙型から複数の値を同時に取得できます。私は仕事からコードの例を投稿することはできませんが、「Flags enum」(「Flags」は列挙型をビット単位で使用するように定義するC#の方法)の簡単なgoogleがこの素晴らしい例を示しています:http:// www.dotnetperls.com/enum-flags


2

ビット並列コンピューティングもあります。データが1と0のみの場合、64個を符号なしのロングロングワードにパックし、64way並列操作を取得できます。遺伝情報は2ビット(DNAのAGCTエンコードを表す)であり、ビット並列方式でさまざまな計算を行うことができれば、そうでない場合よりもはるかに多くのことができます。メモリ内のデータの密度は言うまでもありません-メモリ、ディスク容量、または通信帯域幅が制限されている場合は、圧縮/解凍を考慮する必要があることを意味します。画像処理などの領域に現れる精度の低い整数でさえ、トリッキーなビット並列計算を利用できます。それ自体が芸術です。


1

なぜ見つかったのですか?

それはおそらく、それらがアセンブリ命令に対応しており、時には高レベルの言語の物事に役立つだけだからです。同じことはGOTOJMPアセンブリ命令に対応する恐ろしいものにも当てはまります。

それらの用途は何ですか?

本当に多くの用途に名前が付けられているので、非常にローカライズされていても、最近の使用法を説明します。私は6502アセンブリで多くの仕事をしており、メモリアドレス、値、比較値などをGameGenieデバイスに使用できるコードに変換する小さなアプリケーションで作業していました(基本的にNESのチートアプリケーション)。コードは、ビット操作によって作成されます。


1

最近の多くのプログラマーは、ほぼ無限のメモリーを持つコンピューターに慣れています。

しかし、一部の使用では、すべてのビットがカウントされる小さなマイクロコントローラーをプログラミングします(たとえば、1k以下のRAMしか持っていない場合)。アルゴリズムが必要とする何らかの状態を保持するために必要な抽象化エンティティ。これらのデバイスのIOは、ビット単位で読み取りまたは制御する必要がある場合もあります。

「現実の世界」には、サーバーやPCよりもはるかに多くの小さなマイクロコントローラーがあります。

純粋な理論上のCS型の場合、チューリングマシンはすべて状態のビットに関するものです。


1

ビット演算子の多くの可能な使用法のもう1つ...

ビットごとの演算子は、コードを読みやすくするのにも役立ちます。次の関数宣言を検討してください....

int  myFunc (bool, bool, bool, bool, bool, bool, bool, bool);

...

myFunc (false, true, false, false, false, true, true, false);

コードを書いたり読んだりするときに、どのブールパラメータが何を意味するかを忘れるのは非常に簡単です。また、カウントを忘れてしまうこともあります。このようなルーチンはクリーンアップできます。

/* More descriptive names than MY_FLAGx would be better */
#define MY_FLAG1    0x0001
#define MY_FLAG2    0x0002
#define MY_FLAG3    0x0004
#define MY_FLAG4    0x0008
#define MY_FLAG5    0x0010
#define MY_FLAG6    0x0020
#define MY_FLAG7    0x0040
#define MY_FLAG8    0x0080

int  myFunc (unsigned myFlags);

...

myFunc (MY_FLAG2 | MY_FLAG6 | MY_FLAG7);

わかりやすいフラグ名を使用すると、はるかに読みやすくなります。


1

Unicodeについて何か知っているなら、おそらくUTF-8に精通しているでしょう。多数のビットテスト、シフト、マスクを使用して、20ビットのコードポイントを1〜4バイトにパックします。


0

私は頻繁にそれらを使用していませんが、時々彼らは便利になります。列挙型処理が頭に浮かびます。

例:

enum DrawBorder{None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8}

DrawBorder drawBorder = DrawBorder.Left | DrawBorder.Right;//Draw right & left border
if(drawBorder & DrawBorder.Left == DrawBorder.Left)
  //Draw the left border
if(drawBorder & DrawBorder.Top == DrawBorder.Top)
  //Draw the top border
//...

0

この使用法がまだ記載されているかどうかはわかりません:

illumos(openSolaris)のソースコードを使用して複数の戻り値を0または1に減らすと、または

int ret = 0;
ret |= some_function(arg, &arg2); // returns 0 or 1
ret |= some_other_function(arg, &arg2); // returns 0 or 1
return ret;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.