1. 序章

はい、Kotlinでもビット演算を実行できます。 のような通常のシンボルという事実にもかかわらず << >> | 、 と ^ 、が欠落しているため、Cでできることはすべて実行できますが、クラスの関数を使用します Int長いです

Kotlinコミュニティは、10年間ビット演算子について議論してきました。 このアイデアを支持する人々は、それは彼らの低レベルのドメインの要件であると言います–通常、サウンドまたはビデオ処理。 これに反対する人々は、他のことをより重要視し、ビット単位の記号で混雑しているコードが、それらを許可する言語では完全に読めない場合があることも指摘しています。

ここでは、Kotlinで関数を使用してビット演算を実行する方法を確認しましょう。

2. Kotlinのビット演算とそのJavaカウンターパート

私たちのほとんどは、以前にビット演算に遭遇したことがあります。 メモリを更新して、KotlinとJavaの構文を比較してみましょう。

操作名 Java演算子 Kotlin Int / Long関数
接続詞(および) a&b aとb
論理和(または) a | b aまたはb
排他的論理和(xor) a ^ b a xor b
反転 a.inv()
左シフト <<ビット shlビット
右シフト >>ビット shrビット
符号なし右シフト a>>>ビット ushrビット

 

Javaには、 | = のように、各ビット演算子で変更された代入演算子も含まれています。 Kotlinでは、 a=aまたはbを繰り返す必要があります。

3. ビット演算のしくみ

ビット演算の記述方法がわかったので、オペランドで何をするかについて説明しましょう。

3.1. 接続詞、論理和、および反転

ビット単位接続詞論理和、および反転は、論理的な対応物と同様に機能しますが、オペランドの各ビットに個別に影響します。 したがって、ビット単位の接続詞の場合、これは次のことを意味します。

val a = 0b10011 // 19
val b = 0b11110 // 30
assert(a and b == 0b10010) // 18

ここでは、わかりやすくするために、バイナリ表記で数値リテラルを記録する方法を使用しました。

そして論理和のために:

val a = 0b101001 // 41
val b = 0b110011 // 51
assert(a or b == 0b111011) // 59

排他的論理和の場合、対応するオペランドビットが両方とも0または両方が1の場合、0が得られます。

val a = 0b110101
val b = 0b101010
assert(a xor b == 31) // 11111

そして、反転はすべての中で最も単純です–すべての0は1になり、その逆も同様です。 ただし、 Int タイプは32バイトで、Longは64バイトであることを覚えておく必要があります。 つまり、短い2進数で正しい結果を得るには、でマスクする必要があります。

assert(0b101100.inv() and 0b111111 == 0b010011)

3.2. バイナリシフト

2進数のシフトは、まさにそのとおりです。2進数を取得し、指定された位置数だけ左または右にシフトします。

assert(0b110011 shl 2 == 0b11001100)

左シフトは1つだけですが、2つの右シフトがあります。符号付きと符号なしです。 unsigned右シフトは、数値の左側にゼロをコピーし、右端のビットをドロップします。 負の数をシフトした場合でも、符号なしシフトの結果は常に正です。

assert(-0b1100110011 ushr 22 == 0b1111111111)

符号付き右シフト、ただし、は、数値の左端にあるビットをコピーします。 これは、番号の符号が同じままであることを意味します。

assert(0b1100110011 shr 2 == 0b11001100) // the first 22 bytes are zeroes
assert(0b11111111111111111111110011001101 shr 2 == 0b111111111111111111111100110011)
// the first bit is 1, it means that the number is negative

4. ビット演算のアプリケーション

では、なぜこれらの操作が必要なのですか? これらは本当に基本的なものであり、ほとんどのCPUでは、計算に1サイクルしかかかりません。 右シフトは2ビットによる整数除算を表し、左シフトは2ビットによる乗算を表します。 それらの動作を見てみましょう。

assert(12 shr 2 == 3) // 12 / 2^2 == 12 / 4
assert(3 shl 3 == 24) // 3 * 2^3 == 3 * 8

ビット単位の演算子とは異なり、実際の乗算と除算には複数のサイクルがかかる場合があります。

次に、情報を非常に緊密にパックする機能があります。各ビットは特定の何かを意味します。 次に、マスクとおよび演算子を使用して、この特定のプロパティを確認できます。

val SKY_IS_BLUE_MASK = 0b00000000000001000000000000

fun isSkyBlue(worldProperties: Int): Boolean = 
    worldProperties and SKY_IS_BLUE_MASK != 0

assert(isSkyBlue(0b10011100111101011101010101))

またはを使用すると、さまざまなフラグを組み合わせることができます。

val SKY_IS_BLUE = 0b00000000000001000000000000
val SUN_IS_SHINING = 0b00000000000000100000000000
val skyIsBlueAndSunShines = SKY_IS_BLUE or SUN_IS_SHINING // 0b00000000000001100000000000

5. 結論

ビット演算は、C / C ++、Python、Javaなどの他の主要言語と同様に、Kotlinで使用できます。 これらは、反転 inv()を除いて、IntおよびLongタイプのインフィックス関数として実装されます。 ビット単位の演算は、信号処理、高性能計算、および密集したデータ構造の作成に役立ちます。

いつものように、すべての例はGitHubから入手できます。