整数に対する演算として,今までに四則演算を紹介しました.しかし整数には他にも様々な演算が用意されています.整数を 2 進法として捉えることで,可能な演算の幅が広がります.
ビット表現
整数は,コンピュータの内部で 2 進法を使って表現されているという話をしました.このことについてもう少し詳しく説明します.
10 進法
多くの人が普段使っているのは 10 進法です.
(ただし
たとえば,
なので,
2 進法
一方,コンピュータの中で用いられるのは 2 進法です.
(ただし
たとえば,
なので,
符号なし整数型
u8
において 25 は 00011001
と表されます. println!
マクロの {:b}
を使うと, 2 進法での表現を出力することができます( b は binary の頭文字です).
fn main() {
println!("{:08b}", 25_u8);
}
00011001
よって,
値 |
u8 での表現 |
---|---|
00000000 |
|
00000001 |
|
00000010 |
|
11111111 |
符号付き整数型
たとえば 8 ビット符号付き整数型 i8
で 11100111
となります.これが, i8
における
fn main() {
println!("{:08b}", -25_i8);
println!("{:08b}", 231_u8); // 同じ
}
11100111
11100111
値 |
i8 での表現 |
同じ表現の u8 値 |
---|---|---|
10000000 |
||
10000001 |
||
10000010 |
||
11111101 |
||
11111110 |
||
11111111 |
||
00000000 |
||
00000001 |
||
00000010 |
||
01111111 |
また,この表を見ると,最上位ビット(一番左の桁)が 0 なら 0 以上の値, 1 なら負の値になっていることが分かります.
ビット演算
ビットごとの AND
bool
型の AND は,「どちらも true
のとき true
となる」演算でした.
一方, true
を 1 , false
を 0 に置き換えることで,整数 0 / 1 についても AND を考えることができます.このとき AND は「どちらも 1 のとき 1 となり,それ以外のとき 0 となる」演算となります.
型が同じ 2 つの整数値 x
, y
について x & y
と書くと,各ビットごとに AND 演算が行われます.たとえば型がともに i8
で 11101100
, 10111010
となるので, x & y
はこの各ビットごとに AND 演算を行った結果,すなわち 10101000
となります.
たとえば最上位ビット(一番左の桁)に着目すると, x
でも y
でも 1 なので, x & y
でも 1 になります.一方,下から 3 番目のビット(右から 3 桁目)に着目すると, x
では 1 ですが, y
では 0 なので, x & y
では 0 になっています.
10101000
は i8
において -20 & -70
の結果は
fn main() {
assert_eq!(-20_i8 & -70_i8, -88_i8);
}
ビットごとの OR
型が同じ 2 つの整数値 x
, y
について x | y
と書くと,各ビットごとに OR 演算が行われます.たとえば型がともに i8
で
となります. 11111110
は i8
で
fn main() {
assert_eq!(-20_i8 | -70_i8, -2_i8);
}
ビットごとの XOR
型が同じ 2 つの整数値 x
, y
について x ^ y
と書くと,各ビットごとに XOR 演算が行われます.たとえば型がともに i8
で
となります. 01010110
は i8
で
fn main() {
assert_eq!(-20_i8 ^ -70_i8, 86_i8);
}
ビット反転
整数値 x
について !x
と書くと,各ビットごとに NOT 演算が行われます.たとえば型が i8
で
となります. 11100110
は i8
で
fn main() {
assert_eq!(!25_i8, -26_i8);
}
シフト演算
左シフト
左シフトは,ビットを「左にずらす」演算です.たとえば, i8
型の
x << y
と書くと, x
を左に y
ビットシフトした値になります.
fn main() {
assert_eq!(100i8 << 2, -112i8);
}
右シフト
右シフトは,ビットを「右にずらす演算」です.右シフトには >>
演算子を使います.左シフトと違って,右シフトは符号なし整数型と符号付き整数型の間で違いがあります.
論理シフト
符号なし整数型の右シフトでは,ビットを右にずらし,左には 0 を詰めます.たとえば, u8
型の
100101
の部分が右に 2 つ動き,空いた左の 2 桁には 0 が詰められています.これを,論理シフトといいます.
fn main() {
assert_eq!(150_u8 >> 2, 37_u8);
}
算術シフト
符号付き整数型の右シフトでは,ビットを右にずらし,左には「もとの数の最上位ビット」を詰めます.
たとえば, i8
型の 00110010
と表され,最上位ビットは 0 です.これを右に 2 ビットシフトすると
となります. 001100
の部分が右に 2 つ動き,空いた左の 2 桁には 0 が詰められています.
一方, i8
型の 11001110
と表され,最上位ビットは 1 です.これを右に 2 ビットシフトすると
となります. 110011
の部分が右に 2 つ動き,空いた左の 2 桁には 1 が詰められています.
このように,左にもとの数の最上位ビットを詰めるような右シフトを,算術シフトといいます.
fn main() {
assert_eq!(50_i8 >> 2, 12_i8);
assert_eq!(-50_i8 >> 2, -13_i8);
}
シフト演算子 <<
>>
についても,対応する複合代入演算子 <<=
>>=
があります.
演算子の優先順位
ここまでで出てきた各演算子を優先順位の高い順に並べると,次のようになります.
優先順位 | 演算子 | 結合順序 |
---|---|---|
高い | 負号 - NOT 演算 ! 参照 & &mut 参照外し *
|
単項 |
︙ | 型変換 as
|
左結合 |
︙ | 乗除算 * / %
|
左結合 |
︙ | 加減算 + -
|
左結合 |
︙ | シフト演算 << >>
|
左結合 |
︙ | AND 演算 &
|
左結合 |
︙ | XOR 演算 ^
|
左結合 |
︙ | OR 演算 |
|
左結合 |
︙ | 比較 == != < > <= >=
|
非結合的 |
︙ | 短絡評価 AND 演算 &&
|
左結合 |
︙ | 短絡評価 OR 演算 ||
|
左結合 |
低い | 代入 = += -= *= /= %= &= |= ^= <<= >>=
|
右結合 |
負の数に付ける -
やビット反転の !
は単項なので,結合順序がありません.また,比較演算子も a < b < c
のように続けて書くことができないので結合順序がありません.