부동소수점 잘 알고 쓰자

Overview

부동소수점으로 표현한 수가 실수를 정확히 표현하지 못하고

부동 소수점 연산 역시 실제 수학적 연산을 정확히 표현하지 못하는 것은 여러가지 문제를 낳는다.

Error of floating point example

0.1과 0.01을 표현하지 못하므로 0.1의 제곱이 0.01이 되지도 않고

0.01과 가장 가까운 수가 되지도 않는다.

24비트 단정밀도 표현에서,

십진수 0.1 은

지수 = -4, 가수 = 110011001100110011001101 이고 그 값은

정확히 0.1000000014901161193847656256이다.

이 수를 다시 제곱하면,

정확히 0.010000000298023226097399174250313080847263336181640625이다.

단정밀도 부동 소수점 (반올림 있는) 하드웨어에서 제곱을 한다면,

정확히 0.010000000707805156707763671875이다.

하지만 0.01과 가장 가까운 표현 가능한 실수는

정확히 0.009999999776482582092285156250이다.

Irrational on floating point number

π (및 π/2)를 표현하지 못하므로 tan(π/2)가 무한대의 값이 나오지 않으며 오버플로(overflow)가 생기지도 않는다.

따라서 π/2를 정확히 표현하지 못하기 때문에 일반적인 부동소수점 하드웨어에서는 tan(π/2)를 계산하는 일이 불가능하다.

C 언어에서 아래의 계산 결과는 16331239353195370.0 가 된다.

double pi = 3.1415926535897932384626433832795;
double z = tan(pi/2.0);

단정밀도에서는 (tanf 함수를 이용하여), −22877332.0 라는 결과를 얻는다.

같은 이유로 sin(π)는 0이 되지 않고

C 언어의 double type 배정밀도에서 약 0.1225×10-15

C 언어의 float type 단정밀도에서 −0.8742×10-7가 된다.

Commutative law & on floating point number

부동소수점 덧셈과 곱셈은 모두 교환법칙 (a + b = b + a 이고 a × b = b × a)이 성립한다.

Associative law on floating point number

결합법칙은 언제나 성립하는 것은 아니다.

즉, (a + b) + c 이 항상 a + (b + c) 과 같지는 않게 된다.

예를 들면 7자리 부동소수점(Float 7) 10진수 계산을 할 때

1234.567 + 45.67846 = 1280.245
1280.245 + 0.0004 = 1280.245

그러나

45.67846 + 0.0004 = 45.67886
45.67886 + 1234.567 = 1280.246

Distribution law on floating point number

항상 분배법칙이 성립하는 것은 아니다.

즉, (a + b) × c 은 a × c + b × c과 다를 수 있다.

1234.567 × 3.333333 = 4115.223
1.234567 × 3.333333 = 4.115223
4115.223 + 4.115223 = 4119.338

그러나

1234.567 + 1.234567 = 1235.802
1235.802 × 3.333333 = 4119.340

Error case on floating point numbers

유효 숫자를 잃어버리는 문제 뿐만 아니라,

π와 0.1를 정확하게 표현하지 못하는 문제와 다른 약간의 부정확성이 다음과 같은 현상을 일으킨다.

  1. 소거: 거의 같은 두 값을 빼는 것은 정확성을 매우 많이 잃게 된다.

이 문제가 아마도 가장 일반적이고 심각한 정확도 문제이다.

  1. 정수로의 변환 문제:

(63.0/9.0)을 정수로 변환하면 7이 되지만 (0.63/0.09)는 6이 된다.

이는 일반적으로 반올림 대신 버림을 하기 때문이다.

  1. 제한된 지수부
    결과값이 오버플로되어 무한대값이 되거나 언더플로되어 비정규 값 또는 0이 될 수 있다.

만약 비정규 값이 되면 유효숫자를 완전히 잃어버린다.

  1. 나눗셈이 안전한지 검사하는데 문제가 생김

즉, 제수(나눗수)가 0이 아님을 검사하는 것이 나눗셈이 오버플로되고 무한대값이 되지 않는 걸 보장하지 않는다.

  1. 같음을 검사하는데 문제가 생김:

수학적으로 같은 계산결과가 나오는 두 계산 순서가 다른 부동소수점 값을 만들어낼 수 있다.

프로그래머는 어느정도의 허용 오차를 가지고 비교를 수행하지만, 그렇다고 해서 문제가 완전히 없어지지 않는다.

Error message on C program

복잡한 지수함수에 승수에도 복잡한 계산 값을 가진 연산을 넣으면,

연산 범위를 초과하여 double, float 값으로 나타날 때,

C Program 에서 Exception 메시지를 출력하고 프로그램이 종료되는 경우가 있다.

참고

위키 - 부동소수점

영문 위키 - 부동소수점 정확도 문제 최소화 방안