Study_Cat

꾸준히 공부하는 고양이가 될게요.

끊임없는 노력은 천재를 이긴다.

코딩/C, C++

[C/C++] 부동소수점 - 컴퓨터는 정확하다며...

Study_Cat 2024. 3. 30. 13:30

우리 컴퓨터는 모든 데이터를 2진수로 저장한다. 그리고 컴퓨터는 "정확하고 빠르다." 일 터.. 가끔 백준의 수학 문제 중 계산 문제가 틀리는 경우도 많고.. 연구 분야에서도 이러한 일로 오류가 발생하곤 한다. 그러면 왜 이런 오류가 발생하고 어떠헥 해결할 수 있는지 소개하고자 한다.

 

1. 오차 원인

컴퓨터는 수를 이진수로 나타낸다. 이 때 정수 부분은 어느 수준까지 유한하기에 나타낼 수 있지만 그와 달리 소수 부분은 무한 소수처럼 매우 긴 경우... 이를 다 저장할 수 없다. 그리고 안타깝게도 

f = 3.145646546228 -> output : 3.1456465721... [소수점 10자리 까지 출력]

 

위와 같은 예시처럼 그 뒤의 소수점을 날리는 형태가 아니라 그냥 값이 다르다. 이 원인 또한 이진수로 나타내기 때문인데 쫌 더 자세히 알아보자!

 

2. 부동소수점

해당 개념을 이해하기 위해 예시를 설명하면서 알아보고자 한다. ex) 13.6875를 나타내보자!

 

1) 이진수 표현

이진수 표현은 $a_i2^i$ 꼴로 표현할 수 있다. 따라서 13 = 8 + 4 + 1 => 1101 , 0.6875 = 0.5 + 0.125 +  0.0625 = 1011

따라서 13.6875 = 1101.1011 로 나타낼 수 있다. 단 01101.1011 등... 으로는 나타내지 않는다!

 

2) 정규화

이진수로 나타낸 숫자를 정규화해보자. 정규화는 abc.def 로 주어졌을 때 a.bcdef * (2^2)꼴로 바꾸는 행위이다. 예시를 나타내면 1101.1011 => 1.1011011 * (2^3) 으로 나타낼 수 있다. 

 

3) 부동소수점 표현

부동 소수점에는 부호부 / 지수부 / 가수부 3개의 구간으로 나뉜다. 그렇다면 해당 부분들이 어떤 역할을 하는지 예시를 나타냄으로 써 이해해보자

출처 : wikipedia

[1] 부호부분

1.1011011 * (2^3) 의 부호는 +이므로 0이다. 만약 -였다면 1이다.

 

 

[2] 지수부분

1.1011011 * (2^3) 의 3 + 127을 이진수로 저장한 값, 즉 10000010이 된다.

 

이 때 왜 127을 더해야 하는지 의문이 생길 수 있다. 표현하는 수가 1미만의 수라면 (2^n)으로 정규화할 때 n이 음수일 수 있다. 하지만 음수를 나타내기 위해서 부호 비트는 1개 더 차지하면 적은 수의 길이 밖에 표현할 수 없다. 따라서 음수를 양수로 만들기 위해 127을 더해서 나타낸다.

 

 

[3] 가수부분

1.1011011 * (2^3) 에서 1. 을 제외한 뒤의 자리 비트들이다. 즉 1011011인 셈이다.

 

물론 부호부분/지수부분/가수부분의 길이는 정해져있기에 이를 고려해서 나타낸다면

0 10000010 10000010000000000000000

 

로 나타낼 수 있다.

 

 

4. 절대오차 / 상대오차

가끔 백준을 풀다보면 이런 이상한 말이 나오는데 이건 다 부동소수점에서 발생하는 오차 때문에 나온 말이다.

 

절대오차 : 실제 값과 차이
상대오차 : 실제 값과 계산값의 비

 

5. 해결방법

1. 쫌 더 큰 자료형 사용 [long long double]

2. 직접 구현 

3. fixed - 특정 소수점 자리 까지만 출력

4. round - 반올림