시프트 연산이란, 비트를 좌우로 밀거나 당기는 연산자이다. 비트가 밀렸을 때 정수 오버플로 되거나 최상위 비트 (부호비트)의 전환으로 인해 비트 이동시 비트가 반대 전환 되는 경우 등의 주의점이 있다.
오른쪽 피연산자 (이동 수) 는 반드시 양의 정수이다. 음수값을 갖는 경우 프로그램이 비정상 종료된다.
피연산자 값 만큼 전체 비트를 왼쪽으로 밀어낸다. 비트가 이동되어 빈 자리는 0으로 채워지고, 자릿수를 벗어난 비트는 버려진다.
2 << 1 = 4 // 1칸 밀면 2배
2 << 2 = 8 // 2칸 밀면 4배
2 << 3 = 16 // 3칸 밀면 8배
결과가 타입의 표현 범위를 벗어나면 2의 승수배가 나오지 않는다. 아래의 예시를 보자.
var y int8 = 64
pirntf("y:%08b y <<2: %08b y<<2: %d\n", y, y <<2, y<<2)
- int8타입의 64 값을 가진 y를 2진수 표현하면 01000000인데 이를 두 칸 시프트 이동하면 8비트 정수의 범위를 넘어서 범위를 벗어난 값은 버려지고 0000 0000이 된다. 즉 0이 된 것.
왼쪽에 추가되는 비트는 최상위 비트값과 같은 비트값이 추가 된다. 즉 부호있는 정수라면 왼쪽 비트에 부호와 같은 값으로, 부호 없는 정수라면 0으로 채워진다.
- 음수: 최상위 비트가 1
양수: 최상위 비트가 0
package main
import "fmt"
func main() {
var x int8 = 16 // 1)
var y int8 = -128 // 2)
var z int8 = -1 // 3)
var w uint8 = 128 // 4)
fmt.Printf("x: %08b x>>2: %08b x>>2: %d\n", x, x>>2, x>>2)
fmt.Printf("y:%08b y>>2: %08b y>>2: %d\n", uint8(y), uint8(y>>2), y>>2)
fmt.Printf("z:%08b z >>2: %08b z>>2: %d\n", uint8(z), uint8(z>>2), z>>2)
fmt.Printf("w:%08b w>>2: %08b w>>2: %d\n", uint8(w), uint8(w>>2), w>>2)
}
// 1) 00010000 x >> 2: 00000100, x>>2: 4
// 2) 10000000 y >> 2: 11100000, y>>2: -32
- 부호는 그대로, 음수부호여서 이동한 자리까지 1로 채워지고 그대로 4로 나눠져서 -32가 된다.
// 3) 11111111 z >> 2: 11111111, z>>2: -1
- 값의 경계에서는 올바른 2의 승수로 나눈 값을 표현하지 못한다. -1을 오른쪽으로 2만큼 시프트 하면 -1을 4로 나눈 값이 아니라 여전히 -1이 된다. 그래서 항상 >> 연산자를 승수로 나눗셈한 결과와 같다고 생각하면 안된다.
// 4) 10000000 w >> 2: 00100000, w>>2: 32
- 부호 없는 1바이트 정수타입 uint8, 128은 2진수로 푝현하면 1000 0000이고 이를 >>2하면 시프트 맨 왼쪽에 0이 채워져서 0010 0000이 된다. ; 128/4