Language/Python

[04-2] Python - 논리연산자/비트연산자 (boolean/bitwise operation)

TechNote.kr 2016. 12. 30. 13:11
728x90

언듯 보기에 논리연산자와 비트연산자가 유사해서 그 차이점이 헷갈렸다. 


논리연산자의 "and"와 비트연산자의 "&"는 결국 같은 "그리고"가 아닌가?

논리연산자의 "or"와 비트연산자의 "|"는 결국 같은 "혹은"이 아닌가?

논리연산자의 "not"과 비트연산자의 "~"는 결국 같은 "아닌"이 아닌가?


몇가지 실험을 통해 그 차이점을 확인해 보았다. 


결론적으로 논리연산자는 영어로 하였을 때 boolean 연산자로 TRUE, FALSE를 위한 연산자이다. 따라서 숫자를 이용한 비트 연산에 사용할 경우 잘못된 값을 도출할 수 있다.  비트 연산을 할 때는 꼭 비트 연산자를 사용해야 한다.

TRUE와 FALSE가 아닌 숫자에 논리연산자를 사용하더라도 에러가 발생하지 않고, 의도치 않은 결과가 계산되기에 더욱 사용에 유의해야 한다. 


[차이점 비교]

1. and와 &의 차이점

2. or와 |의 차이점

3. not과 ~의 차이점


[기타 bit 연산자]

4. shift(<<, >>) 살펴보기

5. xor 살펴보기




차이점 확인을 위해 Decimal 240 (0b11110000) 을 base로 0b10101010, 0b1010 2개값을 mask 로 사용한다.



1. 'and' 와 '&' 의 차이점


[& 기본 동작]

1 & 1 = 1

0 & 1 = 1

1 & 0 = 1

0 & 0 = 0





0b11110000 (value)

0b10101010 (mask0)

-------------- (&)

0b10100000 (result)


0b11110000 와 0b10101010 을 & 연산하였을 때 예상되는 값은 0b10100000 이다. 

하지만 "and"를 이용한 연산을 하였을 경우 0b10101010 이라는 잘못된 값이 나오고, "&" 를 이용한 연산을 하였을 경우 0b10100000 으로 정상적인 값이 도출되었다. 



0b11110000 (value)

0b00001010 (mask1)

-------------- (&)

0b00001010 (result)


이번에는 masking을 할 때 자리수가 어떻게 되는지 궁금해서 자리수를 다르게 해서 연산해 보았다. 결국 자리수가 맞지 않을 경우 앞에 0을 넣어 다른 수와 자리수를 맞춰서 계산을 하고 계산 결과 앞에 0이 있을 경우 추후에 제거해 보여준다. 앞의 결과와 마찬가지로 "&" 연산의 경우 정상적인 결과를 보여주었지만 "and"의 경우 0을 출력하였다. 



2. 'or' 와 '|' 의 차이점


[| 기본 동작]

1 | 1 = 1

0 | 1 = 0

1 | 0 = 0

0 | 0 = 0




0b11110000 (value)

0b10101010 (mask0)

-------------- (|)

0b11110000 (result)


'|' 의 경우는 앞의 '&'의 경우와 다소 다른 결과를 보여주었다. 'or' 이건 '|' 이건 간에 동일한 결과를 확인할 수 있었다. 



0b11110000 (value)

0b00001010 (mask1)

-------------- (|)

0b11111010 (result)


두번째 예제의 경우도 마찬가지로 동일한 결과를 얻을 수 있었다. 



3. 'not' 와 '~' 의 차이점




 'not'의 경우는 어느 정도 이해할만한 결과를 보여주었다. 흔히 0이 아닌 값을 TRUE라고 보았을 때 0이 아닌 값에 not을 붙이면 FALSE이니 0을 결과로 보여주는 것이다. 




 '~' 의 경우는 사실 좀 생소한 결과를 얻긴하였는데, 결론적으로 맞는 결과이긴 했다. 사실 처음 예상했던 값은 각각의 bit 들이 0이면 1로, 1이면 0으로 변경되는 값을 생각했었다. 


~(101010) -> 010101


하지만 '~'을 붙이게 되면 2의 보수를 표현해주는 듯하다.

python document 를 보면 '~'의 동작에 대해 아래와 같이 설명하고 있다. 


The bitwise inversion of x is defined as -(x+1).


~(0b11110000) = -(0b11110000 + 1) = -0b11110001


의도된 값을 보여주고 있다. 


혹시라도 1의 보수 즉, 11110000 -> 00001111 의 변환이 필요하다면 '~'가 아닌 xor을 통한 변환이 필요하다.


0b11110000 (value)

0b11111111 (mask)

-------------- (xor)

0b00001111 (result)



4. shift(<<, >>) 살펴보기




 Shift 연산의 경우는 bit 를 이동시켜 주는 역할을 한다. 

0b11110000 << 2 의 경우 왼쪽으로 2칸 옮기는 역할임으로 0b1111000000 을 결과로 보여준다.

앞에서 언급하였듯이 이진 표현 앞에는 0이 생략된 것임으로 아무런 손실없이 이동하게 된다.


즉, 0b0011110000 이므로 << 2 를 하게 되면 0b1111000000 이 된다.


>> 2 의 경우도 마찬가지이다. 다른 점은 이진 표현의 오른쪽은 0이 생략된게 아니라는 점이다. 따라서 오른쪽으로 이동하게 되면 범위를 벗어난 부분은 없어지게 된다.


즉, 0b11110000 이므로 >> 2 를 하게 되면 0b111100 이 된다.




5. xor 살펴보기


[^ 기본 동작]

1 ^ 1 = 0

0 ^ 1 = 1

1 ^ 0 = 1

0 ^ 0 = 0




 xor의 경우는 다른 'and, &', 'or, |', 'not, ~' 와는 다르게 논리 연산에 xor이라는 것은 없다. 따라서 헷갈리거나 실수할만한 부분은 없다. 


0b11110000 (value)

0b10101010 (mask)

-------------- (xor)

0b01011010 (result)



0b11110000 (value)

0b00001010 (mask)

-------------- (xor)

0b11111010 (result)


 위와 같이 정상적으로 계산됨을 확인할 수 있다. 




728x90