バランスの取れたプログラマーは、ビット単位の操作をどの程度必要としますか?[閉まっている]


34

私は最近いくつかのOpenJDKコードを参照していますが、ビット単位の操作に関係する興味深いコードを見つけました。StackOverflowで質問したこともあります。

ポイントを説明する別の例:

 1141       public static int bitCount(int i) {
 1142           // HD, Figure 5-2
 1143           i = i - ((i >>> 1) & 0x55555555);
 1144           i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
 1145           i = (i + (i >>> 4)) & 0x0f0f0f0f;
 1146           i = i + (i >>> 8);
 1147           i = i + (i >>> 16);
 1148           return i & 0x3f;
 1149       }

このコードはIntegerクラスにあります。

これを見ると、私は愚かな気持ちにならざるを得ません。大学で1つまたは2つのクラスを欠席しましたか、それとも私がちょうど取得することになっているものではありませか?単純なビット単位の操作(ANDing、ORing、XORing、shiftなど)を実行できますが、上記のようなコードはどのように思い付きますか?

バランスの取れたプログラマーは、ビット単位の操作をどの程度必要としますか?

副次的注意事項... 私が心配しているのは、StackOverflowで私の質問に答えた人が数分で答えたということです。彼がそれをできたら、なぜ私はヘッドライトで鹿のようにじっと見つめたのでしょうか?


4
どのような種類の開発作業をしていますか(または、今それを行っていない場合は行いたいですか)?これはWeb開発では役に立たないと思いますが、組み込みシステムでのビット単位の操作がたくさん動作するのを見てきました。
トーマスオーエンズ

26
ユーザーインターフェイス開発やWeb開発を行うために誰かを雇っている場合、ビット操作は私が尋ねるようなものではありません。ただし、ネットワークプロトコル、組み込みシステム、およびデバイスドライバーの操作に精通している人がそれに慣れていることを期待します。
トーマスオーエンズ

11
一体何が>>>オペレーターですか?
DeadMG


3
// HD, Figure 5-2私が見たいと思う最初のものです。ファイルの冒頭のコメントによると、HDですHenry S. Warren, Jr.'s Hacker's Delight
シュナダー

回答:


38

バランスの取れた開発者として、演算子とビット単位の操作を理解する必要があると思います。

したがって、少なくとも、少し考えてから上記のコードを理解できるはずです。

ビット単位の操作はかなり低レベルになる傾向があるため、WebサイトやLOBソフトウェアで作業している場合、それらをあまり使用することはほとんどありません。

他のことと同じように、それらをあまり使わないなら、あなたはそれらに精通していないでしょう。

したがって、誰かが(おそらく)この種のコードを頻繁に使用するので、誰かが非常に迅速にそれを理解できることを心配するべきではありません。おそらくOSコード、ドライバーコード、またはその他のトリッキーなビット操作を記述しています。


1
+1:ビット単位の操作は、どの開発者にとっても重要な知識です(しゃれを意図していません)、現在は特定の状況で本当に非常に重要です。あなたが日常的にそれらに出会ったことがないなら、一般的な知識を持っている方がそれらを軽視するよりも優れています。その脳のスペースを解放してください。
ニコラススミス

また、それらをいつ使用するかを理解し、それらが当面の問題の正しい解決策である場合、それらの使用を敬遠しないでください。
-user606723

@ user606723のコメントに追加するには-ビット単位のものが通常使用され、多かれ少なかれ一般的に遭遇する実際にはいくつかの場所があります-ハッシュ(およびそれに関連するもの)とRGBの特定の色の抽出/設定に保存されますint。たとえば、特定のレジスタから返されたビットフラグをチェックすることでCPU情報を読み取ることができますが、これにはasmが関係し、通常は必要に応じてより高いlvlラッパーがあります。
TC1

36

あなたのような問題を解決する方法を理解していれば、あなたがチェックするビット演算子の理解を十分に持っている「整数値は、ビット7-12で示される「クリアビット5「ビット3と8がセットされているかどうかを確認」」または」見つける缶を「バランスのとれた」チェックリストのツイドルビットボックス。

あなたの例にあるのは、整数のような小さなデータを操作するための高性能アルゴリズムのコンパイルであるHacker's Delightから来ています。そのコードを最初に書いた人は誰でも、5分でそれを吐き出しませんでした。その背後にある物語は、ビットをカウントするための高速でブランチフリーの方法が必要であり、著者はビットの文字列をじっと見つめて問題を解決する方法を作り上げるのに時間を費やした可能性が高いです。彼らが以前にそれを見ていない限り、誰もそれがどのように機能するか一目で理解するつもりはありません。ビット単位の基本をしっかり理解し、コードの実験に時間を費やすことで、コードがどのように機能するかを理解できるでしょう。

これらのアルゴリズムを理解していなくても、それらが存在することを知るだけで「丸み」が増します。たとえば、高性能ビットカウントを扱うときが来ると、何を勉強すべきかがわかるからです。Googleより前の世界では、これらのことを知るのはずっと困難でした。今ではキーストロークが不要です。

SOの質問に回答したユーザーは、以前に問題を見たことがあるか、ハッシュを学習したことがあります。彼に書いて聞いてください。


少なくともこれらのことを知っていることで+1。多くのことを少し知っているのは良いことです。業界の人々がこのようなことについて話し始めたら、議論されていることを少しでも知らない部屋の人になりたくないでしょう。
maple_shaft

3
上記のコードコメントの略語「HD」を解決するための+1。
ペテルトレック

私はこの種のものが大好きで、HDブックを注文しました。参照いただきありがとうございます。
tcrosley

8

あなたの例から、本当に考えずに絶対に知っておくべきことがいくつかあります。

1143 i = i-((i >>> 1)&0x55555555);

ビットパターン0x555 ...を交互ビットパターン0101 0101 0101として認識し、演算子がそれを1ビット(右)オフセットしていること、および&がマスキング操作(およびマスキングの意味)であることを認識する必要があります。

1144 i =(i&0x33333333)+((i >>> 2)&0x33333333);

再びパターンです。これは0011 0011 0011です。また、今回は2をシフトし、再びマスキングしています。シフトとマスキングは、認識すべきパターンに従っています...

1145 i =(i +(i >>> 4))&0x0f0f0f0f;

パターンが固まります。今回は00001111 00001111であり、もちろん、今回は4にシフトしています。マスクのサイズだけシフトするたびに。

1148 return i&0x3f;

別のビットパターン、3fはゼロのブロックとそれに続く1の大きなブロックです。

あなたが「まあまあ」なら、これらのことはすべて一目でわかるはずです。使用するとは思わないかもしれませんが、これを知らないとコードを大幅に簡素化する機会を逃す可能性があります。

より高いレベルの言語でも、ビットパターンを使用して、より小さなフィールドに大量のデータを格納します。これは、ゲームで常に127 / 8、63 / 4、255 / 6の制限が表示される理由です。フィールドを詰めずに10倍も使用することを余儀なくされるほど多くのこれらのものを保存する必要があるためです。メモリ量。(まあ、究極は、膨大な数のブール値を配列に格納する必要がある場合、それについて考えなかった場合に使用するメモリ量の32から64倍を節約できます-ほとんどの言語は、このレベルで快適に感じられない人は、単に未知のものが怖いという理由だけで、このようなデータを保存する機会に抵抗します。

また、ネットワークを介して配信されたパケットを手動でパック形式で解析するなど、恐れることなく簡単なことも避けます。これにより、1kパケットを必要とするゲームが200バイトまで必要になります。小さいパケットはネットワークをより効率的にスライドし、待ち時間を短縮し、より速い相互作用速度を可能にします(ゲームの新しいプレイモード全体を可能にします)。


5

ビデオフレームを操作するためのソフトウェアでコードを見たことがあるので、たまたまコードを認識しました。オーディオおよびビデオCODEC、ネットワークプロトコル、チップレジスタなどを定期的に使用している場合、ビット単位の操作が多く発生し、2番目の性質になります。

作業がこれらのドメインと頻繁に一致しない場合でも、気分を悪くするべきではありません。私はビット単位の操作をよく知っていますが、レイアウトと重みと拡張に関するすべての癖のために、GUIを書く必要があるまれな機会に速度を落とします。あなたの強みは、あなたが最も多くの経験を持っている場所です。


4

知っておくべき主なことは、整数がどのように表現されるか(一般に、長さがプラットフォームに依存する固定長ビットベクトル)と、それらで利用可能な操作です

主な算術演算を+ - * / %理解する必要はありませんが、マイクロ最適化には便利ですが、ほとんどの場合、コンパイラーがそれを処理します。

ビット操作セットを使用| & ~ ^ << >> >>>するには、少なくとも通過する理解が必要です

ただし、ほとんどの場合、それらを使用してビットフラグをメソッドに渡すだけで、OR一緒にingを渡してintをAND渡してから、長いパラメーターリストで複数(最大32)のブール値を渡すよりも読みやすくなります。インターフェイスを変更せずに変更可能なフラグ

言うまでもなく、ブール値は、フラグのようにまとめてパックするのではなく、通常、バイトまたは整数で個別に保持されます


コードスニペットに関しては、ビットの並列カウントを行うため、アルゴリズムO(log(n))は、nが単純なループではなくビット数で実行できるようになります。O(n)

最初のステップは、理解することが最も難しいですが、あなたはそれがビットシーケンスを交換する必要があることをセットアップから起動する場合0b000b000b010b010b100b01して0b11まで0b10、それはフォローに容易になります

最初のステップでは、等しくなるi - ((i >>> 1) & 0x55555555)ようにiすると0b00_01_10_11、この出力は次のようになります。0b00_01_01_10

(に0x5等しいことに注意してください0b0101

IUF私たちが取る私は= 0b00_01_10_11この手段0b00_01_01_10 - (0b00_00_11_01 & 0b01_01_01_01)0b00_01_10_11 - 0b00_00_01_01順番になると0b00_01_01_10

(i & 0x55555555) + ((i >>> 1) & 0x55555555)同じ結果が得られたかもしれませんが、これは1つの追加操作です

次の手順も同様です


4
このコードの最も重要な品質は、ブランチがないことです。これにより、複雑さを軽減するよりもさらに大きなメリットが得られます。
サイモンリヒター

3

誰もが基本的なビット単位の操作を理解する必要があります。多くの練習を必要とする、最適化された堅牢な方法でタスクを実行するための基本的な操作の構成です。

埋め込まれた人々のように、ビット操作を毎日行う人は、もちろん、強力な直感と巧妙なトリックを開発しようとしています。

低レベルの処理を行わないプログラマーは、ビット単位の操作でどの程度のスキルを身に付ける必要がありますか?あなたが貼り付けたようなスタンザで座って、頭の体操やパズルのようにゆっくりと作業するのに十分です。

同様に、組み込みプログラマーは、Web開発者がビット単位の操作について理解しているのと同じくらい、httpについても理解すべきだと思います。言い換えれば、ビット操作を常に使用しているのでなければ、ビット操作に慣れないのは「OK」です。


3
実際、場合によっては、組み込みプログラマーは、Web開発者よりもhttpについて理解する必要があります(私は両方を行います)。Web開発を行う場合、通常は何らかのタイプのフレームワークを利用できます。インターネットに接続されたデバイスを操作する組み込み開発者として、httpスタックをゼロからコーディングする必要がありました。
tcrosley

@tcrosely、はい、あなたは絶対に正しいです。「http」よりも良い例は、「ORM」や「JEE」のようなものでしょう。主なポイントは、彼らが定期的に実践しない限り、一般的にいくつかの主題について習得することはできないということです。
アンジェロ

私は同意し、ORMまたはJEE(J2MEと呼ばれていたJMEのみ)に対処する必要はありませんでした。
tcrosley

3

ハッカーの喜びは派生的な作品です。すべての祖先は1972年のHakMemです。http: //w3.pppl.gov/~Hammett/work/2009/AIM-239-ocr.pdf

重要なことは、あらゆるタスクの明白なアルゴリズムが必ずしも最良ではないことを知ることです。多くの場合、特定の問題に対するエレガントな解決策の存在を知ることが重要です。



3

ビットごとの演算子の解釈はどれくらい難しいですか?

私は組み込みシステムをプログラムしています。私はこれをたくさん練習しました。コードを含むハッシュマップに関するリンクされた質問

static int hash(int h) {
   // This function ensures that hashCodes that differ only by
   // constant multiples at each bit position have a bounded
   // number of collisions (approximately 8 at default load factor).
   h ^= (h >>> 20) ^ (h >>> 12);
   return h ^ (h >>> 7) ^ (h >>> 4);
}

コードを声に出して指示するのと同じくらいの時間で、私にとって完全に理にかなっています。で説明されてbitCountいるイベントはすぐにわかりますが、実際にビットをカウントする理由を解明するのに1分かかります。ただし、コメントは素晴らしいものであり、ハッシュの問題よりもコードが何をするのかを理解するのが少し難しくなります。

コードを読んで理解することを区別することが重要です。私はbitCountコードを解釈し、それが何をするかを読みとることができますが、それがなぜ機能するのか、それが機能するのかを証明するのに1分かかります。コードをスムーズに読むことと、コードがそのままの理由を理解することには違いがあります。一部のアルゴリズムは単純に困難です。hashコードは意味を成しますが、コメントは説明し、なぜ、何が行われていました。ビットごとの演算子を使用する関数を理解するのが難しい場合、落胆しないでください。形式に関係なく難しいと思われる数学的なことを行うのによく使用されます。

アナロジー

私はこのことに慣れています。私が慣れていない1つの主題は正規表現です。 私は時々ビルドスクリプトでそれらに対処しますが、日常の開発作業では決して扱いません。

正規表現の次の要素を使用する方法を知っています。

  • [] 文字クラス
  • *.、および+ワイルドカード
  • ストリングの始まりとストリングの^終わり$
  • \ d、\ w、および\ s文字クラス
  • / gフラグ

これは単純なクエリを作成するのに十分であり、私が見るクエリの多くはこれから遠く離れていません。

このリストにないものはすべて、チートシートを探します。何でも、つまり、{}および()-チートシートでは不十分です。ホワイトボード、リファレンスマニュアル、そしておそらく同僚が必要になることを知るには、これらの人々について十分に知っています。いくつかのクレイジーなアルゴリズムをいくつかの短い正規表現にまとめることができます。

既知の要素のリストにないものを必要とする、または提案する正規表現を設計するために、認識されると予想される入力のすべてのクラスをリストし、それらをテストスイートに入れます。多くの断続的なステップを使用して正規表現をゆっくりと増分的に作成し、これらのステップをソース管理にコミットするか、コメントに残しておくと、後で壊れるときに何が起こるかを理解できます。実動コードにある場合は、より経験のある人がレビューするようにします。

これは、ビット演算子を使用している場所ですか?

丸みを帯びたいですか?

私の推測では、紙を引き出すか、ホワイトボードに行って操作を手動で実行することで、このようなコードが何をするかを解釈できるなら、あなたはバランスのとれた資格を得ます。ビット演算の分野で優れたバランスの取れたプログラマーとしての資格を得るには、次の4つのことができる必要があります。

  1. 流動的一般的な操作を読み書きすることができる
    アプリケーションプログラマのために、ビット演算子を持つ一般的な操作は、基本的な演算子を含める|&セットとクリアフラグにします。これは簡単なはずです。次のようなものを読み書きできるはずです

    open('file', O_WRONLY | O_APPEND | O_CREAT );
    // Use an OR operator ^ here and ^ here to set multiple flags
    

    速度を落とすことなく(フラグの意味を知っていると仮定します)。

  2. いくつかの作業でより複雑な操作を読み取ることができ
    ます。分岐なしでO(log(n))時間でビットを非常に高速にカウントし、hashCodesでの衝突の数が制限された量だけ異なるようにし、電子メールアドレス電話番号、または正規表現を含むHTMLは難しい問題です。これらの分野の専門家ではない人がホワイトボードに手を差し伸べることは合理的です。理解するために働き始めることができないのは不合理です。

  3. 多くの作業で複雑なアルゴリズムを書くことができる
    あなたが専門家でない場合、複雑で難しいことをできると期待すべきではありません。ただし、優れたプログラマーは、継続的に作業することでそれを達成できるはずです。これを十分に行うと、すぐに専門家になります:)


2

あなたがまともな大学に行った場合、あなたは離散数学の授業を受ける必要があったはずです。2進、8進、および16進の算術および論理ゲートを学習しているはずです。

そのことに注意してください。混乱しているのは普通です。私がWebアプリケーションを書いているのであなたに慰めがあるなら、私は主にこのようなコードを見たり書いたりする必要はほとんどありませんが、バイナリ算術とビット演算子の動作を理解しているので十分な時間を与えられて、ここで何が起こっているのかを最終的に理解することができます。


2

携帯電話のプログラマーとして、私はこの種のことに対処しなければなりませんでした。デバイスにあまりメモリがない場合や、伝送速度が重要な場合は、かなり一般的です。どちらの場合も、できるだけ多くの情報を数バイトにパックしようとします。

いくつかの低レベルのWindowsのものはビットを詰め込んでいますが、PHPの5年ほどでビット単位演算子を使用したことは思い出せません(おそらく私だけかもしれません)。

あなたは「これを見ると、私は馬鹿だと感じずにはいられません」と言います。怒らないでください。

カウボーイプログラマーの出力に会ったばかりです。

彼は保守可能なコードを書くことを何も知りませんか?私は彼が一年以内にこれに戻って、それが何を意味するかを覚えてみなければならない人であることを心から願っています。

コメントをカットしたか、コメントがなかったのかはわかりませんが、このコードは私がQ / A Q / Aマネージャーであったコードレビューに合格しませんでした(数回行ったこともあります)。

これが良い経験則です-コードで許可される唯一の「裸の整数」は0 1番目1です。他のすべての数値は、言語に応じて#defines 、cost、enumなどでなければなりません

これらの3と0x33333333がNUM_WIDGET_SHIFT_BITSやWIDGET_READ_MASKのようなことを言っている場合、コードは読みやすくなります。

オープンソースプロジェクトでこれを公開した人は恥を知れませんが、個人的なコードのコメントであっても、意味のある定義/列挙を使用し、独自のコーディング標準を持っています。


16進定数も許容されると考えます。 0xFF00は、私よりもはるかに読みやすいです0b1111111100000000。設定されているビット数を判断するためにカウントする必要はありません。
ケビンフェルメール

1

この特定のコードは、図Hacker's Delightの図5.2 から直接取り出したものです。Cでのオンライン(ポップ機能)はこちら。現在、著者は更新バージョンの使用を推奨していることに注意してくださいhttp : //www.hackersdelight.org/HDcode/newCode/pop_arrayHS.c.txt

これらの種類のマイクロ最適化を学びたい場合は、その本をお勧めします。その楽しみですが、非常に低レベルのビットプログラミングを頻繁に行わない限り、おそらく理解できないでしょう。そしてほとんどの場合、コンパイラーはこれらの種類の最適化の多くをあなたのために行うことができます。

また、これらの種類のアルゴリズムを理解し、テストケースまたは2つでそれらを処理するために、すべての16進数を2進数で書き換えることも役立ちます。


1

例による説明。データはビットのシーケンスです。次の操作を使用できるバイト01001101のビットをカウントします。1.最後のビットの値を確認できます。2.シーケンスをシフトできます。

  1. 01001101->最後のバイトは1、合計= 1です。シフト
  2. 10100110->最後のバイトは0、合計= 1です。シフト
  3. 01010011->最後のバイトは1、合計= 2です。シフト
  4. 10101001->最後のバイトは1、合計= 3です。シフト
  5. 11010100->最後のバイトは0、合計= 3です。シフト
  6. 01101010->最後のバイトは0、合計= 3です。シフト
  7. 00110101->最後のバイトは1、合計= 4です。シフト
  8. 10011010->最後のバイトは0、合計= 4です。シフト

私たちの答え:4。

これは難しくありませんでしたか?ビット単位演算の大きな問題は、実行できることが限られていることです。少し直接アクセスすることはできません。しかし、たとえば、最後のビットの値をMASK 00000001と比較して知ることができ、すべてのビットをシフト操作で最後のビットにすることができます。もちろん、結果として得られるアルゴリズムは、慣れていない人にとっては恐ろしく見えるでしょう。インテリジェンスとは関係ありません。


0

あなたがしている仕事が関連していない限り、私はあなたがそれを必要とは言いません:

  • オーディオ処理
  • ビデオ処理
  • グラフィックス
  • ネットワーキング(特にパケットサイズが重要な場合)
  • 膨大なデータ

システムのアクセス許可モデルが特に複雑な場合や、読みやすさを犠牲にしてすべてを1バイトに詰め込みたい場合は、Unixスタイルのフラグにアクセス許可を保存することも別の用途です。

これらの分野は別として、開発者/上級開発者がビットシフトを実演できれば、大きなプラスとして数えます。&および^職業への関心を示しているため、より安定した信頼性の高いコードにつながると言えます。

前述のとおり、メソッドを「取得」しない限り、メソッドの実行内容と背景についての説明が必要です。私はそれが知性に関連しているとは言いませんが、日常的に16進法で作業し、特定のパターンが解決できる問題を認識することにどれほど精通していますか。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.