Java에서 모든 Byte Stream 관련 클래스들은 InputStream, OutputStream에 기반을 두고 있다. 이 두 클래스는 abstract class이고 이에 따른 abstract method가 딱 하나있다. read메서드다. 그런데 의문점이 든다. 분명 byte 관련한거니까 return 값이 byte여야 하는거 아닌가? 왜 int일까? 링크에 첨부된 Javadoc에서는 다음과 같이 설명한다.
Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned.
byte는 0~255까지 값으로 나타낼 수 있다. (1byte는 8bit고 2진수로 00000000~11111111까지 나타낼 수 있기 때문이다. 그리고 여기에서 얘기하는 0~255는 Java에서의 byte가 아니라, 저수준의 데이터를 표현할 때 쓰는 unsigned byte를 의미한다.)
이 0~255까지를 데이터를 표현하면 되지만, 파일을 더 이상 읽어들이지 못하는 끝지점을 표현할 수 있는 수단인 EOF를 표현할 수 없었다. 그래서 이 byte에서 int로 변환시켜 표현하는 것을 택했다. 그리고 그 EOF의 값은 일반적으로 -1로 표현한다. 그렇다면 0~255의 값중에서 -1을 표현하고 싶으면 어떻게 해야할까? 이것에 대한 수많은 자료들 중 Ben Eater의 Twos complement: Negative numbers in binary만큼 잘 설명하는 자료를 찾아보진 못했다. 아래에서는 이 동영상을 참고하여 쓴 글이다.
간단하게 여기서부터는 1~7까지만 생각해본다.(8은 2^3으로 다 표현하려면 4칸을 넘어버려 명확하게 설명하는데 조금 방해된다.) 0그리고 1~7까지의 숫자를 나타내보면 다음과 같다.
그리고 음수를 표현해주기 위해 아무런 역할을 안하고 있는 맨 앞자리를 Sign Bit로 둔다. 이 sign bit가 0이면 음수를 1이면 양수를 나타낼 수 있게 된다.
그러면 이제 검증을 해보자. 1 + (-1) 은 몇이 되나.
0001 + 1001 => 1010 => 1010에 해당하는 숫자는 -2다. 한가지 문제가 더 있는데, 0은 부호가 없어서 두 가지의 값이 나타나면 안된다. 하지만, 그림을 보면 0000, 1000 이렇게 두가지가 나타내질 수 있다는 문제점이 생긴다.
Ones Complement는 Sign Bit때와 양수 그리고 0은 똑같이 나타낸다. 하지만, 음수를 나타낼 때는 숫자를 완전히 뒤집는다. 0은 1로 1은 0으로 뒤집어 음수를 정의한다.
마찬가지로 검증을 해보자. 1 + (-1) 은 1111 이 된다.(실제로 해보면 10000이나 네자리까지만 반영되기 때문에 마지막 1은 고려하지 않는다.) 하나만 더 해보자. 4 + (-4) 는? 1111이 된다. 즉, 부호가 반대인 것끼리 더하면 0이 된다. 하지만 여전히 0000이란 값의 0이 하나 더 있었다. 이 문제는 5 + (-3) 같은 곳에서 나온다.
0 1 0 1 :5
1 1 0 0 :-3
--
0 0 0 1 :1(위의 그림을 보면 대응되는 값이다.)
결국 0이 두가지가 있기 때문에 예상했던 값(2) 보다 한칸 밀리는 1이 나오게 된 것이다.
그렇다면 결국 Ones Complement 에서 0이 두 개인 문제만 해결하면 된다. 그리고 이것을 해결하기 위해 음수들과 0인 0~-7을 한칸씩 밀어 -1~-8로 나타내었다. 한칸씩 밀어냈다는 뜻은 1을 더해줬다는 말과 같다.
마지막으로 검산을 통해 증명해보자. 1 + (-1) 은 0(10000)이 된다. 7 + (-7) 은 0이 된다.
위의 과정을 쭉 따르면 결국 1을 -1로 만들 수 있다.
1. 1(0001)의 Ones Complement로 -1을 나타낸다. (0을 1로 1을 0으로) -1(1110)
2. Two Complement로 -1을 나타내주기 위해 1을 더해준다. -1(1111)
이렇게 OutputStream,InputStream 에서 EOF를 표현하기 위해, 1을 -1로 바꾸고, byte를 int로 변환한다.