Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでのビット単位のハック

通常、日常業務でビット単位の計算を行う必要はない可能性があります。 Rubyのビット単位のANDおよびOR演算子(&および|)は、意図的ではなく偶然に使用される可能性があります。誤って入力していないのは誰ですか?

しかし、Cやアセンブラー、または私の場合はTurbo Pascalのような低レベルの言語をプログラミングして育った場合は、おそらく少なくとも少し手を加えたことがあるでしょう。

問題に対するビット単位の解決策はクールです。コンピューターが実行できる最も基本的な操作(2進数学)を使用して問題を解決できる場合、それ以上にエレガントになることはありません。

Rubyでのバイナリの操作

コンピュータ内のすべてが数値として表され、それらの数値は2進形式であることをおそらくご存知でしょう。しかし、それはルビーではどのように見えますか?この例では、Rubyを使用して文字「a」のASCII文字コードを検索し、それを「バイナリ」として出力しています。

Rubyでのビット単位のハック ordを使用できます Rubyのメソッドを使用して文字のASCIIコードを取得し、printfを使用します それの「バイナリ」表現を印刷します。

数値を2進数で表示するには、printfなどのメソッドを使用する必要がありますが、常に2進数でした。 0b11111111のような構文を使用して、Rubyコード内に2進数を書き込むことができます。 。

Rubyでのビット単位のハック コードにバイナリリテラルを追加するには、1と0の文字列の前に0bを付けます。したがって、0b11111111

バイナリ値の操作

ルビーでバイナリリテラルを使用する方法がわかったので、それらを試してみることができます。そのために、ビット演算子を使用します。

あなたはおそらく&&のようなブール演算子に慣れているでしょう。式a&&bは、aとbの両方がtrueの場合にのみtrueを返します。ビット演算子は非常に似ています。

たとえば、ビット単位のANDは2つの値を取り、それらをビットごとに比較します。両方のビットが1の場合、対応する出力ビットを1に設定します。そうでない場合、ゼロに設定します。したがって、8ビットの場合、8つの別々のANDが発生します。ビットが1つある場合は、ANDが1つあります。次の例は、単一ビットを使用してこれを示しています。

Rubyでのビット単位のハック 単一ビットを使用したビット単位のAND演算の例。

2ビットでも同じように機能します。

Rubyでのビット単位のハック ビットの各ペアは個別にアンディングされます

ルビーのビット演算子

ほとんどすべてのプログラミング言語には、このビット演算子のセットが付属しています。それらに精通していない場合は、IRBで少し時間をかけて試してみてください。それらを学習すると、残りの人生でそれらを使用できるようになります。

OperatorDescriptionExample

ビット単位のAND演算子 両方の入力値で結果が1の場合、結果のビットを1に設定します 0b1010 & 0b0111 == 0b0010
| ビット単位のOR演算子 いずれかの入力値が1の場合、結果のビットを1に設定します 0b1010 | 0b0111 == 0b1111
^ ビット単位のXOR演算子 結果ビットがいずれかの入力値で1であるが、両方ではない場合、結果ビットを1に設定します 0b1010 | 0b0111== 0b1101
ビット単位の逆演算子 入力が1の場合、結果ビットを0に設定し、その逆も同様です。 ~0b1010 == 0b0101
<< ビット単位の左シフト演算子 。入力ビットを指定した桁数だけ左に移動します。 0b1010 << 4== 0b10100000
>> ビット単位の右シフト演算子 。入力ビットを特定の数の場所で右に移動します 0b1010 >> 4 == 0b0000

実用:構成フラグ

わかりました、わかりました、これはビット単位の数学の最も退屈な使用でなければなりません。しかし、それは最も一般的なものの1つでもあります。 Java、C、またはC ++で記述されたコードとインターフェースをとる必要がある場合は、最終的にビット単位の構成フラグに遭遇します。

1996年で、データベースシステムをゼロから構築したと想像してみてください。映画「サイバーネット」を見たばかりなので、何らかのアクセス制御を組み込むのが良いかもしれないと思います。

ユーザーがDBで実行できるアクションは、読み取り、書き込み、削除、その他5つです。それぞれを個別に設定できるようにする必要があります。読み取りはできるが書き込みや削除はできないユーザーがいる可能性があります。テーブルを書き込むことはできるがドロップすることはできないものがあるかもしれません。

これらの構成フラグを格納する最も効率的な方法は、1バイトのビットとして使用することです。次に、ユーザーはそれらを単純にORして、必要な権限の組み合わせを作成します。

MYDB_READ   = 0b00000001 # These numbers are called bitmasks
MYDB_WRITE  = 0b00000010
MYDB_DELETE = 0b00000100
MYDB_INDEX  = 0b00001000

user.permissions = MYDB_READ | MYDB_WRITE

ちなみに、これはUNIXファイルのパーミッションの処理方法と非常によく似ています。ファイルを読み取り専用にするためだけにマジックナンバーを使用する必要があるのはなぜか疑問に思ったことがある方は、ご存知でしょう。

特定のビットが設定されているかどうかを検出できない限り、構成オプションをビット単位で格納することはあまり役に立ちません。これを行うには、ビット単位のANDを使用します。

Rubyでのビット単位のハック ビットが設定されているかどうかを判断するには、ビットマスクでANDを使用します。

あまり実用的ではない使用法(Cプログラマーでない限り)

それでは、数学の魔法を使ってみましょう。

歴史的に、ビット操作は、ある計算の最後の1ミリ秒を絞り込もうとしたときに使用されていました。ご想像のとおり、グラフィックプログラマーや、何よりもパフォーマンスを必要とするその他の人々によって多く使用されていました。

したがって、このような手法は、日常のRuby開発には実用的ではありません。しかし、それでも楽しい学習演習であり、組み込みシステムのプログラミングなどに取り組む場合は、合法的に役立つ可能性があります。より徹底的な治療については、このビットいじりハックのリストを確認してください。

2の累乗による乗算と除算

数値1、2、4、および8の2進表現を見てみましょう。ご覧のとおり、数値を2倍にすることは、すべてのビットを1桁左にシフトすることと同じです。同様に、数を半分にすることは右にシフトすることと同じです。

Rubyでのビット単位のハック 1、2、48のバイナリ表現

思い出してください。左シフト演算子と右シフト演算子があります。つまり、ビットをシフトするだけで、2の累乗で乗算および除算できるということです。

Rubyでのビット単位のハック ビット単位のシフト演算子を使用して、2の累乗で乗算または除算できます

2つの正の整数の平均化

次のように、2つの整数の基本的な加算を行うことができます:(x + y)==(x&y)+(x | y)==(x ^ y)+ 2 *(x&y)。平均を計算するために必要なのは、右シフト演算子を介して取得できる2で少し割るだけです。

Rubyでのビット単位のハック 次のように2つの正の整数を平均化できます:(x&y)+((x^y)>>1)

高速逆平方根

最も伝説的な例の1つを含めずに、ビットをいじるブログ投稿を行うことはできませんでした。これは、1999年のQuake3アリーナソースコードからのジョンカーマックの高速逆平方根近似です。アルゴリズムは彼のものではありませんが、最も有名な実装はです。

これをRubyに直接移植しようとはしません。これは、CからRubyに直接変換できない方法で浮動小数点数のバイナリ表現を構築することによって機能するためです。

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
    i  = 0x5f3759df - ( i >> 1 );               // what the fuck? 
    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. Rubyでのラムダの使用

    ブロックはRubyの非常に重要な部分であり、ブロックなしで言語を想像するのは難しいです。しかし、ラムダ?ラムダが好きなのは誰ですか?あなたはそれを使わずに何年も行くことができます。まるで過ぎ去った時代の遺物のようです。 ...しかし、それは完全に真実ではありません。ラムダを少し調べてみると、ラムダにはいくつかの興味深いトリックがあります。 この記事では、ラムダの使用法の基本から始めて、さらに興味深い高度な使用法に移ります。したがって、ラムダを毎日使用していて、それらについてすべて知っている場合は、下にスクロールするだけです。 Lambdasについて覚えておくべき主なことは、それらが関数の

  2. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,