Java 언어로 TCP 소켓을 통해 IP 정보를 바이트 배열로 전송하는 로직을 구현하고 있었다. 숫자 167을 바이트 배열에 담아 전송하기 위해서 (byte)167 이렇게 강제 형 변환 과정을 거쳤다. 변환된 값을 출력해서 어떻게 전송되는지 확인해보려고 했는데 -89 라는 값이 표시되었다. 왜 내가 저장한 167이 -89로 출력 되는 것일까? 기초적인 내용이지만 이번 기회에 깔끔하게 정리하고 넘어가기로 했다.
정리하는 과정에서 컴퓨터는 0과 1밖에 모르는 초고속 바보라는 사실을 오랜만에 되새길 수 있었다. 우선 Java에서 int 자료형은 4 byte 자료형이다. int를 byte로 강제 형 변환하면 앞의 3 byte는 제외하고 마지막 1 byte만 살아남는다. 나는 IP 정보를 바이트로 변환하는 중이었고, IP 정보는 0~255 범위의 숫자 4개로 표현되기 때문에 숫자 하나를 단순히 byte 강제 형 변환으로 처리해도 문제되지 않았다.
1 바이트는 8 개의 비트로 이루어지는데, 자바에서 byte는 부호 있는 정수로 표현된다. 그래서 첫 번째 비트는 부호를 나타내며 1이면 음수, 0이면 양수로 인식하고 두 번째 비트부터 여덟 번째 비트가 정수 값을 나타낸다. 즉, 0111 1111 은 127이고, 1111 1111은 -127이다. 그러면 128 이상의 숫자는 저장할 수 없는 걸까?
저장할 수 있다. 저장은 되는데 출력해보면 좀 이상한 값으로 출력 된다. 167은 이진수로 1010 0111이다. 그러면 혹시 -32가 출력 되는 걸까? -(1+2+4+32)
아니다. 이상하게도 -32가 아니라 -89로 출력 된다.
Why? 컴퓨터가 음수를 나타낼 때 2의 보수라는 과정을 거치기 때문이다. 그 과정은 간단하게 외울 수 있다. 첫 번째 비트가 1이면 비트를 반전 시킨 뒤에 1을 더한 값을 표시한다.
- 비트 반전 (1의 보수):
1010 0111→0101 1000 - 1 더하기 (2의 보수):
(0101 1000) +1=0101 1001 - 십진수 변환:
0101 1001을 십진수로 바꾸면 89다. (1+8+16+64) - 부호 적용: 앞에 -를 붙이면 최종 결과는 -89가 된다.
167은 이진수로 1010 0111이지만 java의 byte 자료형은 이를 음수로 인식하여 2의 보수를 취한 값을 보여주게 되어 -89가 표시되는 것이다.
