코린이의 소소한 공부노트

정수의 오버플로우(overflow) 본문

Java

정수의 오버플로우(overflow)

무지맘 2021. 12. 16. 22:29

에버랜드에서 알바를 하는 상상을 해보자.

"환상~의 나라↗ 에버랜드로~ 안녕하세요~ 안녕하세요~"

한 손은 열심히 흔들면서 사람들을 반기고, 다른 손으로는 카운터를 사용해 몇 명이 입장하는지 열심히 세고 있을 것이다.

그 카운터의 숫자가 올라가는 것을 살펴보면 다음과 같다.

[그림 009] 카운터

빨간색 글씨로 된 부분을 보자. 9999에서 한번 더 누르면 10000이 돼야 정상인데, 자릿수가 4자리밖에 없으므로 1을 빼고 0000이 되는 것이다. 이것이 이번 글에서 설명할 오버플로우와 아주 깊은 관련이 있다.

 

우리가 일상적으로 사용하는 수는 10진수이고, 컴퓨터가 사용하는 수는 2진수이다.

[그림 010] 10진수와 2진수

자리수가 몇인지에 따라 표현 가능한 범위가 다르다. 그리고 이전 글에서 봤듯이 어떤 타입이냐에 따라서도 그 범위가 다르다. 우리는 정수 변수를 선언할 때 해당 타입의 범위 내의 값을 하나 정해서 변수 초기화를 하는데, 그 범위를 넘어가면 에러가 발생한다.

byte b = 128; // byte의 범위는 -128 ~ 127
>>
Type mismatch: cannot convert from int to byte

128은 byte의 범위를 벗어나기 때문에 int로 취급되고, byte의 범위가 int 범위의 부분집합이기 때문에 int를 byte로 변환할 수 없다는 에러 메시지이다.

이번에는 다른 방법으로 b에 128을 대입해보겠다.

byte b = 127; // 127
b = (byte) (b+1); // -128

int를 byte로 형변환(type casting)하여 대입하니 에러 없이 대입되었는데, 그 값이 -128이다. 이게 어찌 된 일일까?

 

오버플로우는 표현 가능한 범위를 넘는 것을 의미한다. 그래서 보통은 범위를 넘어서는 계산을 하게 되면 에러가 발생하는데, 형변환을 해서 계산하면 자연스럽게 계산이 된다. 그 계산방식은 순환방식이다.

[그림 011-1] 4자리 10진수 순환방식과 오버플로우

빨간 화살표는 +1, 파란 화살표는 -1을 뜻한다. 0000부터 1씩 더하면 0001, 0002 등을 거쳐 9999까지 커지고, 9999부터 1씩 빼면 9998, 9997 등을 거쳐 0000까지 작아진다. 왼쪽 띠를 끝부분이 붙게 둥글게 말면 오른쪽처럼 되는데, 이 끝부분이 만나는 곳에서 오버플로우가 발생한다. 즉 최댓값 + 1 = 최솟값, 최솟값 - 1 = 최댓값이 되는 것이다.

 

이는 2진수에도 마찬가지이다. 4 bit 2진수를 위의 그림과 같은 형태로 표현해보겠다.

[그림 011-2] 부호 없는 정수와 부호 있는 정수의 4 bit 2진수 순환방식

여기서도 마찬가지로 최댓값 + 1 = 최솟값최솟값 - 1 = 최댓값이 된다. 이제 이 코드의 결과가 왜 -128이 되는지 이해가 된다.

byte b = 127; // 127
b = (byte) (b+1); // -128