논리연산자 && vs &, || vs |
@&& vs &
1
2
3
4
5
6
7
8
9
10
11
12
boolean a = false;
boolean b = false;
// &&
boolean r = a && (b = true);
System.out.println("a=%b, b=%b, r=%b\n", a, b, r);
// a=false, b=false, r=false
// &
r = a & (b = true);
System.out.printf("a=%b, b=%b, r=%b\n", a, b, r);
// a=false, b=true, r=false
@|| vs |
1
2
3
4
5
6
7
8
9
10
11
12
boolean a = true;
boolean b = false;
// ||
boolean r = a || (b = true);
System.out.printf(a=%b, b=%b, r=%b, a, b, r);
// a=true, b=false, r=true;
// |
r = a | (b = true);
System.out.printf(a=%b, b=%b, r=%b, a, b, r);
// a=true, b=true, r=true;
&&, ||의 경우 앞의 피연산자의 값으로 결과를 알 수 있다면 뒤의 명령은 실행하지 않는다. 위의 코드에서 a의 값만 보고도 결과가 판단이 되기 때문에 (b = true) 할당 명령을 실행하지 않았다. 반면, &. |경우 앞의 피연산자로 결과를 알 수 있을 지라도, 뒤에 놓은 경령까지 모두 실행한다.
비트 연산자
비트 연산자의 활용
비트연산은 마스킹, 오버레이 기법, 색조 변경 등 이미지 및 영상 처리에 사용된다. 비트 단위 연산을 사용하면 연산의 가장 작은 단위인 비트 수준에서 연산을 수행하기 때문에 정보를 효율적으로 저장할 수 있기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
<노란색 강화>
10001100 10010010 10101100 (변경전 색)
| 10010010 01001001 00000000 (빨강, 초록빛 강화)
-------------------------------
10011110 11011011 10101100 (결과값)
<노란색 약화>
10001100 10010010 10101100 (변경전 색)
& 11111011 11111101 11111111 (빨강, 초록빛 약화)
-------------------------------
10001000 10010000 10101100 (변견한 색)
색을 표현하기 위해 컴퓨터는 RGB 각 색상을 8비트로 표현한다. 이때 |
와 &
연산을 활용하면 각 색에 대한 비트를 통과시키거나, 빼거나, 더하여 색감을 조절할 수 있다. 위의 첫번째 비트 연산은 기존 이미지의 노란색을 강화하여 빈티지한 느낌을 주는 필터 역할을 하고 있다. 비트연산으로 색을 다루는 것을 코드로 구현해보자.
1
2
3
4
5
6
7
// 빨간색 제거
int pixel = 0x003f448;
System.out.println(pixel & 0x0000ffff);
// 파란색 강화
int pixel = 0x003f4478;
System.out.println(pixel | 0x00000057);
비트 연산자 &와 %
나머지값을 구하는 % 산술연산자를 사용한 연산을 & 비트연산자를 사용한 연산으로 바꿀 수 있다. 정확히 말하면, 두 번째 피연산자가 2의 제곱수일 때 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
System.out.println(11 % 2); //1
System.out.println(11 & 0b1); //1
// 0000001011
// & 0000000001
// -------------
// 0000000001
System.out.println(11 % 4); //3
System.out.println(11 & 0b11);//3
// 0000001011
// & 0000000011
// -------------
// 0000000011
System.out.println(11 % 8); //3
System.out.println(11 & 0b111);//3
// 0000001011
// & 0000000111
// -------------
// 0000000011
주석을 보면 알 수 있듯. 비트 연산자 &는 두번째 피연산자에 0을 준 부분은 빼고, 1을 준 부분은 그대로 통과시켜 추출하는 효과가 있기 때문이다. 즉, 어떤 값에 대해 2의 n승으로 나눈 나머지 값을 구하고 싶다면, & 연산자를 이용하여 그 값의 하위 n비트 값만 추출하면 된다.
따라서 & 연산자를 이용하면 다음과 같이 짝수/홀수를 알아내는 코드를 만들 수 있다.
1
2
System.out.println((37 & 0x1) == 0 ? "짝수" : "홀수");
// 홀수
비트 이동 연산자
«
- 왼쪽으로 비트를 이동시킨다.
- 오른쪽 빈 자리는 0으로 채운다.
- 왼쪽 경계를 넘어간 비트는 자른다.
»
- 오른쪽으로 비트를 이동시킨다.
- 왼쪽 빈 자리는 부호기호로 채운다. (음수면 1, 양수면 0)
- 오른쪽 경계를 넘어간 비트는 자른다.
»>
- 오른쪽으로 비트를 이동시킨다.
- 왼쪽 빈자리는 음수 양수에 상관 없이 0으로 채운다.
- 오른쪽 경계를 넘어간 비트는 자른다.
비트 이동의 유효 범위
1
2
3
4
System.out.println(5 << 1); //10
System.out.println(5 << 33); //10
System.out.println(5 << 65); //10
System.out.println(5 << 97); //10
5에 1비트를 이동한 값과 33비트, 65비트, 97비트를 이동한 값은 같다. 이유는 int는 32비트(4바이트)의 메모리를 사용하기 때문이다. int 타입의 값에 대해 비트 이동을 할 때는 0~31까지만 유효하다. 31을 넘는 경우 32로 나눈 나머지 값을 비트 이동으로 간주한다. 같은 원리로 64비트를 사용하는 long 타입의 경우 비트이동은 0~63까지 유효하고, 63을 넘는 경우 64로 나눈 나머지 값을 비트 이동으로 간주한다.
비트 연산자를 사용하여 true/false 값 저장하기
true/false 값을 저장하기 위해서 어떤 방법을 쓸 수 있을까?
별도의 변수에 저장 => 32바이트 소요
배열 사용 => 8바이트 소요
(boolean 배열을 JVM에서 다룰 때는 각 boolean에 대해 1바이트 사용)
비트 연산자 사용 => 4바이트로 최대 32개 데이터 저장 가능
00000000 00000000 00000000 00000000
각 비트에 true false 값 저장
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final int JAVA = 0x0001; //0000 0001
final int C = 0x0002; //0000 0010
final int PYTHON = 0x0004; //0000 0100
final int KOTLIN = 0x0008; //0000 1000
final int SQL = 0x0010; //0001 0000
final int GO = 0x0020; //0010 0000
// Java, Python을 할 줄 아는 개발자의 정보 설정
int dev = JAVA | PYTHON;
// 검사하기
System.out.printf("Java :%b\n", (dev & JAVA) == JAVA); //true
System.out.printf("C :%b\n", (dev & C) == C); //false
System.out.printf("P :%b\n", (dev & PYTHON) == PYTHON);//true
증감 연산자
할당 연산자
+= -= *= /= %= &= | = ^= «= »= »>= |
할당연산자를 사용하면 코드를 축약할 수 있다.