고정소수점, 부동소수점

이우길·2022년 12월 28일
1

cs

목록 보기
1/1
post-thumbnail

실수 표현 방식

Goal

  • 프로그래밍 안에서 소수점을 사용하는 방식을 이해하기

개요

프로그래밍 언어 안에서는 실수를 표현할 때 주로 부동소수점을 사용하게 된다.
대표적으로 Java와 float와 double을 주로 사용하고 Javascript에서는 number를 이용한다.

하지만 이러한 타입들을 이용하여 연산을 할 때 오차가 발생할 수 있기 때문에 신경써야 한다.


이진기수법

컴퓨터는 0과 1로 이루어진(2진법) 기계어를 사용한다. 하지만 사람은 수를 표현할 때 기본적으로 10진법을 이용한다.

정수의 경우에는 간단하게 10진수의 숫자를 2진수로 표현하기 쉽다.

예를들어 10진수인 18을 2진수로 변환하면 1001이 될 것이다.

정수는 위와 같이간단하지만 컴퓨터에서 실수 (real number)를 표현하려면 어떻게 할까?

정수 부분은 위에서와 같이 손쉽게 2진수로 변환을 하면 된다. 문제는 소수점인데 정수부와 동일하게 숫자를 하나씩 2진수로 바꿔버리면 안되나 생각을 할 수 있지만 그렇게 하면 서로 다른 10진수 숫자가 2진수로 변환되었을때 중복이 되는 문제가 발생한다.

1.9 => 1.1001
1.41 => 1.100 1

그렇기 때문에 실수는 정수의 정 반대의 연산을 하게 된다. 정수는 2진법으로 변경하기 위해 2로 나눠가는 반면 실수는 2를 곱해가면서 1이나 0을 얻어오게 된다.

0.625

0.625 * 2 = 1.25 => 1을 빼고 나머지 0.25
0.25 * 2 = 0.5  => 0을 빼고 나머지 0.5
0.5 * 2 = 1  => 1을 배고 나머지 0

위와 같이 2를 계속해서 곱해가다 0이나오면 종료하고 결과를 위에서 부터 읽어준다 즉 0.6250.101이 된다.


고정소수점 (Fixed Point)

고정소수점 표현 방식이라는 것은 쉽게 말해 10진수를 2진수로 바꾸고 그것을 그대로 사용하는 방식이다.

예를 들어 118.625(라는 실수가 있으며 해당 실수를 2진수로 변환하면 1110110.101이 되며 이것을 그대로 저장하게 된다.

16비트 체계를 쓴다고 하면 01110110.10100000이와 같이 저장을 하게 되는데

첫번째 bit는 부호 비트이며 나머지는 정수부와 소수부로 채워지게 된다.

소수부의 경우 앞에서부터 채우게 되며 남는 뒷자리는 다 0으로 채우게 된다.

이러한 고정 소수점 방식은 구현하기 편하지만 사용하는 비트 수 대비 표현할 수 있는 수의 범위가 적고 정밀도가 낮기 때문에 실수를 다룰 필요가 있는 서비스에서는 거의 사용되지 않는다.


부동 소수점 (Floating Point)

부동 소수점 방식은 실수를 2진법으로 변경한 것을 그대로 사용하는 것이 아니라 몇가지 과정을 추가적으로 거친 후 저장을 하게 된다.


정규화

정규화라는 단어는 수학이나 컴퓨터 분야에서 다양한 의미로 쓰이지만 여기서 말하는 정규화라는 것은 2진수를 1.xxx... * 2^n 꼴로 변환하는 것을 말한다.

변환하는 방법은 간단하다. 정수부에서 1만 남을 때 까지 소수점을 왼쪽으로 이동시키고 이동한 칸 수 만큼 위의 식에서 n자리에 집어넣으면 된다. (정수부가 0인 경우에는 오른쪽으로 이동한다.)

예를 들어 위에서 봤던 1110110.101을 정규화 하면 1.110110101 * 2 ^ 6가 된다.


IEEE 754 부동소수점 표현

IEEE 표준에 따르면 부동소수점 방식으로 실수를 저장하는데 32비트 혹은 64비트가 사용되며 32비트 기준으로 아래 그림과 같은 구조를 가지게 된다.


부호 비트는 고정소수점과 동일하게 0이면 양수, 1이면 음수를 의미한다.

정규화 된 결과 소수점 오른쪽에 있는 숫자들을 왼쪽부터 그대로 넣으면 되며 남은 자리는 0으로 그대로 채우게 된다.

지수부는 위의 계산결과(1.110110101 * 2 ^ 6)에서 n의 위치에 있는 6를 2진수로 바꾼 10을 그냥 넣는 것이 아닌 bias라고 하는 지정된 숫자를 더한 다음에 넣어야 한다.

IEEE 표준에서는 32비트를 쓰는 경우 bias127로 규정하고 있다. 따라서 32비트 기준 6 + 127 = 133를 2진수로 바꾼 10000101가 들어가게 된다.

그럼 bias를 왜 쓸까? 그 이유는 지수가 음수가 될 수도 있어서 그렇다. 예를들어 0.000101이라는 이진수가 있을 때 정수부가 0이기 때문에 왼쪽이 아닌 오른쪽으로 소수점을 밀게 되는데 그럼 1.01 * 2 ^ -4가 된다.

위의 결과 (1.01 * 2 ^ -4)에서 -4를 지수에 어떻게 저장할 것인가? 맨 앞 부호비트가 있다고 해도 해당 부호 비트는 지수부 만을 위한 부호비트가 아니다. 그렇기 때문에 bias을 더하여 32비트 기준 0 ~ 127 구간은 음수, 128 ~ 255 구간은 양수를 표현하도록 만든 것이다.


단정도, 배정도

위에서 살펴본 것과 같이 32비트 체계를 32비트 단정도 (Single-Precision), 64비트 체계를 64비트 배전도 (Double-Precision) 라고 부른다.

프로그래밍 언어에서 흔히 접할 수 있는 실수형타입 float, double는 각각 전자, 후자에 해당한다.

float는 부동소수점 방식을 사용하는 기본형이라는 의미로 floating point에서 따왔으며 double는 64비트 배정도를 사용한다는 의미로 double-precision에서 따왔을 것이라 추측된다.

double는 64비트 체계에서 지수부가 11비트, 가수부가 52이다. 지수부가 2 ^ 11 즉 2048개의 수를 표현할 수 있으므로 0 ~ 1023구간은 음수, 1024 ~ 2047 구간은 양수 지수를 의미하며 bias는 1023이 된다.


REFERENCE

profile
leewoooo

0개의 댓글