3.6. 실수 자료형의 이해
1. 실수 연산의 어려움
function countObvious(n) {
let same = 0;
for (let x = 1; x <= n; ++x) {
let y = 1.0 / x;
if (y * x == 1.0) {
++same;
}
}
return same;
}
- \(\frac {1} {x} \times{x}\)인 x의 개수를 세는 함수
- countObvious(50)? 49
버그인가?CPU의 문제인가?- 실수 표현 방식의 문제 O
2. 실술와 근사값
2.1. 정수형
컴퓨터가 모두 정확하게 표현 가능
2.2. 실수형
컴퓨터가 모두 정확하게 표현 불가능
자료형 | 부호비트 | 지수비트 | 가수비트 | 지수범위 | 유효자릿수(십진수) |
---|---|---|---|---|---|
32비트 실수형 | 1 | 8 | 23자리 | \(-2^7+2 \sim 2^7 -1(=-126 \sim 127)\) | 6(float) |
64비트 실수형 | 1 | 11 | 52자리 | \(-2^10+2 \sim 2^10 -1(=-1022 \sim 1023)\) | 15(double) |
80비트 실수형 | 1 | 15 | 64자리 | \(-2^14+2 \sim 2^14 -1(=-16382 \sim 16383)\) | 18 |
- 32비트의 경우: (부동 소수점 상수에 대한 제한
- 지수 비트
- 8비트로 총 256가지 표현 가능하지만, 0 표현 위해 0이 예약되어 있고, 255는 무한대 표현 위해 예약되어 있다
- 실제로 사용할 수 있는 범위는 1부터 254까지이며,
- 양수 127
- 음수 -126
- 지수범위: \(-2^{지수비트-1}+2 \sim 2^{지수비트-1}-1\)
- 유효자릿수(Significant Digit): 표현할 수 있는 정밀도(Precision) 1. 최대: 3.402823466e+38F - 지수부(8): 1111 1110 - 가수부(23): 111 1111 1111 1111 1111 1111 2. 최소: 1.175494351e-38F - 지수부(8): 0000 0001 - 가수부(23): 000 0000 0000 0000 0000 0000
- 지수 비트
2.2.1 문제점
소수점 이하 자릿수 정확도의 문제
- 순환/비순환 무한소수의 경우 소수점 이하 자릿수가 무한함
- 모든 자릿수를 정확하게 표현할 수 없으므로 적절히 비슷한 근사값을 사용
2.2.2. 영향 요소
- 어떤 순서로 계산하는지
- 컴파일러 최적화를 켰는지
x86 아키텍처에서 C++ 컴파일러 최적화 설정에 따라 64비트 실수형 변수를 내부적으로 80비트 실수 레지스터에 담은 채 그대로 사용할 수 있다
- 중간에 로그 메시지 출력하는지
로깅 함수 호출 시 80비트 레지스터에 담긴 값을 스택에 잠시 저장하면서 이 값들이 64비트로 변경되고, 이때 잘려나간 부분에 있던 값들이 답에 영향
3. IEEE 754 표준
가장 많은 컴퓨터/컴파일러들에서 사용하는 실수 표기 방식
3.1. 내용
- 매우 방대한 규모의 표준
- 무한대, 비정규 수(subnormal number), NaN(Not a Number) 등의 특수한 ㄱ밧 존재
- 오버플로와 언더플로 처리
- 반올림에 관한 규정
3.2. 특징
3.2.1. 이진수로 실수 표기
- \(\frac {1} {2^i}\): 소수점 밑 i번째 자리의 크기
- 1011.101
- 1011: \(2^3 + 2^1 + 2^0 = 8 + 2 + 1 = 11\)
- .101: \(\frac{1}{2} + \frac{1}{2^1} = \frac{1}{2} + \frac{1}{8} = 0.625\)
3.2.2. 부동 소수점(floating-point) 표기법
3.2.2.1. 문제점
정수부/소수부에 사용 가능한 비트들을 어떻게 배분할 것인지의 문제
- 정수부에 많은 비트 사용 \(\rightarrow\) 소수부 정확도 떨어짐
- 소수부에 많은 비트 사용 \(\rightarrow\) 큰 수 표현 불가
3.2.2.2. 해결
위 문제 해결 위해 소수점을 옮길 수 있도록 함
3.2.2.2.1. 방식
- 소수점을 적절히 옮겨서 소수점 위에 한 자리만 남도록 한다
- 최상위 비트에서부터 표현할 수 있는 만큼 표시한다
- 나머지는 반올림한다
- 이진법으로 표현되므로, 소수점 앞의 숫자는 1만 가능하므로, IEEE 754에서는 소수점 앞의 1을 제외
3.2.2.2.2. 예제
- \[11.625 \rightarrow 2진법 \rightarrow 1001.101 \rightarrow 1.011101\]
- 1.011101 맨 앞에서부터 표현할 수 있는 만큼 표시
3.2.2.3. 실수 변수 구조
3.2.2.3.1. 저장 정보
- 실수는 \(263.3 = 2.633\times{10^2}\)처럼 정수부와 실수부로 구성
- 부호 비트: 양수/음수 여부
- 지수(exponent): \(10^2\)
- 가수(mantissa, 유효숫자): \(2.633 \times 10^2\)에서 2633이 유효 숫자
3.2.2.3.2. 예제
- 263.3
- 263:
- 정수부
- 1 0000 0111
- 0.3:
- 소수부
- 0.0100 1100 1100 1100
- 263:
- 1 0000 0111 . 01001100110011…
- 1 . 0000 0111 0100 1100 1100 11…: 소수점을 앞을 8회 이동(\(2^8\))
- 부호비트: 0(양수)
- 지수 비트:
- bias(32비트의 경우 127 = 0111 1111) + 지수(소수점 이동 횟수) = 127 + 8 = 135 = 1000 0111
- bias를 더하는 이유는?
- 지수는 부호 있는 값이며, 큰 값과 작은 값 모두 표현 가능
- 하지만 부호 있는 값에서 2의 보수는 비교를 어렵게 함 \(\Rightarrow\) 지수를 부호 없는 수로 저장하여 비교하기 쉽게 만든다
- 즉, 음의 지수 값을 양수로 표현하기 위해 bias를 더한다
- 가수 비트: 0000 0111 01001100110011…
- 최종적으로 비트는 다음과 같다
부호(1) 지수(8) 가수 비트(23)
0 1000 0111 0000 0111 0100 1100 1100 110
실수 비교하기
countObvious 함수
- \(\frac{1}{10}\times{3}\)과 \(\frac{3}{10}\)을 비교할 떄 둘 모두 신수 변수에 정확하게 담을 수 없으며, 표현할 수 있는 가장 가까운 실수로 근사해 표현
- \(\frac{1}{10}\times{3}\) 참 값보다 약간 큰 값
- \[\frac{1}{10}\]
$$ 0.1\times{2} $$ = 0.2, Integral part: 0 $$ 0.2\times{2} $$ = 0.4, Integral part: 0 $$ 0.4\times{2} $$ = 0.8, Integral part: 0 $$ 0.8\times{2} $$ = 1.6, Integral part: 1 $$ 0.6\times{2} $$ = 1.2, Integral part: 1 $$ 0.2\times{2} $$ = 0.4, Integral part: 0 $$ 0.000110...\times{3} $$
- 3 = 0011
- \[0.000110\dots\times{0011}\]
- \(\frac{3}{10}\) 참 값보다 작은 값
- \[\frac{3}{10}\]
$$ \frac{3}{10} $$ $$ 0.3\times{2} $$ = 0.6, Integral part: 0 $$ 0.6\times{2} $$ = 1.2, Integral part: 1 $$ 0.2\times{2} $$ = 0.4, Integral part: 0 $$ 0.4\times{2} $$ = 0.8, Integral part: 0 $$ 0.8\times{2} $$ = 1.6, Integral part: 1 $$ 0.6\times{2} $$ = 1.2, Integral part: 1 0.10011
- \(\frac{1}{10}\times{3}\) 참 값보다 약간 큰 값