우리 컴퓨터는 모든 데이터를 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개의 구간으로 나뉜다. 그렇다면 해당 부분들이 어떤 역할을 하는지 예시를 나타냄으로 써 이해해보자
[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 - 반올림
'코딩 > C, C++' 카테고리의 다른 글
[자료구조] Ordered Linked List 구현하기 (0) | 2024.10.18 |
---|---|
[자료구조] 포인터와 동적 할당, 자료구조 입문하기! (with c언어) (0) | 2024.10.18 |
[C/C++] 자료형의 프로모션(실수 방지!) (0) | 2024.07.10 |
[C/C++] 포인터 완벽 이해 ( 포인터, 배열, 상수, 다중포인터 ) (0) | 2024.06.02 |
[C/C++] 자료형 계산에서 자주하는 실수들 (1) | 2024.03.29 |