[Java] compare 메서드에서 overflow가 발생하지 않는 이유
들어가며
Comparator와 Comparable 인터페이스를 자세히 공부하면서 문득 의문이 들었습니다.
"왜 Integer.compare 메서드를 사용하면 오버플로우가 발생하지 않을까?"
전 처음엔 return 값으로 a - b 형식의 수학적 연산을 사용했습니다. 하지만 금방 오버플로우가 생길 수 있다는 문제점을 알았고 인터넷에 급급히 서치해서 Integer.compare 메서드로 바꿔 사용했습니다. 그런데 Integer.compare는 어떤 방식으로 객체를 비교하길래 오버플로우를 예방할 수 있는 걸까요?
Integer.compare
여기서 말하는 compare는 Comparator의 추상 메서드가 아닌 유틸리티 메서드입니다.
저는 Comparator와 Comparable 인터페이스 공부가 끝나고 바로 자바 표준 라이브러리를 뜯어봤습니다.
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
compare 메서드는 단순히 비교 연산자로 대소를 비교하고 있었습니다.
그런데 곰곰히 생각해보면 비교 연산자는 뺄셈 없이 어떻게 대소를 비교하는 걸까요? 궁금해서 컴공과한테도 물어보고 구글링도 마구마구한 결과. 비교 연산자는 CPU의 상태 플래그를 사용해서 대소를 판단한다는 것을 알아냈습니다!
CPU의 상태 플래그
플래그는 프로세서에서 특정 조건이나 상태를 나타내는 1비트짜리 변수입니다.
상태 플래그의 종류엔
- Zero Flag(ZF) : 두 값이 같으면 1로 설정
- Carry Flag(CF) : 자리올림이 발생했으면 1로 설정 (unsigned 연산에서)
- Sign Flag(SF) : 결과가 음수이면 1로 설정
- Overflow Flag(OF) : 오버플로우가 발생했으면 1로 설정 (signed 연산에서)
이렇게 있습니다. 하지만 컴퓨터 공학을 다루는 블로그는 아니기 때문에 그냥 "상태 플래그가 이런 게 있다~" 정도로만 알아도 될 것 같습니다.
CPU에서 비교 수행 과정
- 자바 코드가 컴파일러에 의해 바이트 코드로 변환되고, 바이트 코드가 JVM에 의해 실행됩니다.
- 이 과정에서 자바 코드에 포함되어 있던 비교 연산은 CPU가 실행할 수 있는 수준의 어셈블리 명령어로 변환됩니다.
- 그리고 CPU는 내부에서 뺄셈 과정으로 비교 연산을 거친 후 위의 4가지 상태 플래그에 결과를 저장합니다.
- 이때, CPU의 뺄셈 연산에선 결과 값을 메모리에 저장하지 않고 상태만 플래그에 저장하기 때문에 오버플로우가 생기지 않습니다!
- JVM은 저장된 플래그 값을 확인하고 프로그램의 흐름을 결정합니다.
CPU 내부에서 뺄셈?
여기까지 읽어보신 분들은 CPU 내부의 뺄셈과 return 문에서 뺄셈이 무슨 차이인지 의문이 들 수 있습니다. CPU 내부에서 이뤄지는 뺄셈에서 결과 값을 메모리에 저장하지 않는다는 점이 중요한 포인트입니다. CPU는 자바 소스 코드에서 비교하려는 객체의 타입이 long인지 int인지, 아니면 BigInteger 인지에 상관없이 32비트 단위로만 연산합니다.
예를 들어, int 자료형을 비교 연산할 때는 32비트 단위의 연산으로 뺄셈을 하고 오버플로우가 발생하면 Overflow Flag(OF)를 설정합니다. 뺄셈의 값은 비교 연산에서 필요하지 않기 때문에 저장하지 않고, 상태 플래그를 통해 비교 결과를 반환합니다.
추가로 BigInteger 타입은 이론상 무한과 가까운 수도 저장할 수 있기 때문에 CPU는 32비트 단위로 나눠서 처리합니다. BigInteger 타입의 객체는 비트 단위로 분할되어 배열로 저장되고, 각각의 요소에 대해 여러 연산을 거친 뒤 크기 비교를 할 수 있습니다.
결과적으로, compare 메서드는 수학적 연산이 아닌 비교 연산을 사용하고 있고, 비교 연산은 CPU 내부에서 뺄셈을 통해 이뤄지기 때문에 overflow가 발생하지 않는 다는 것을 알게 되었습니다.
.
.
.
마치며
자바와 관련된 글과 아주 살짝 핀트가 어긋났을지도 모르지만 전 하나가 궁금해지면 꼬리에 꼬리를 물고 계속 궁금증이 생기는 편입니다. 이번에도 정렬 인터페이스 공부하다가 cpu와 어셈블리어까지 살짝 공부하게 됐습니다. 시간은 많이 들었지만 이해하니 뿌듯하네요!
피드백과 질문은 언제든지 환영합니다!