ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프로그래머] 부동소수점은 실수(実数)가 아니다
    97가지 시리즈/프로그래머 2018.11.02 17:36

    #프로그래머가_알아야_할_97가지 vol.32

     부동소수점은 실수(実数)가 아니다 by. Chuck Allison


    부동소수점수(浮動小数点数)는 수학에서 말하는 실수(実数)와 다릅니다. Pascal과 Fortran 등의 프로그래밍 언어에서는 실수라고 불리울 때도 있지만 엄밀하게 말해서 실수는 아닙니다. 실수는 우선 정밀도가 무한해서 연속적이고 손실도 없습니다. 한편, 부동소수점수 정밀도는 유한하므로 실수라기보다는 오히려 정수에 가깝다고 해도 될 겁니다. 이른바 아주 기묘한 상태의 정수입니다.


    예를 들어, "2147483647"이라는 수(부호가 있는 32bit 정수 최대치)를 32bit 부동소수점형 변수(x라고 합시다)에 대입해 x의 값을 출력 해보면 결과는 "2147483648"이 되어 나옵니다. 'x - 64'로 계산해서 출력 해보면 값은 그대로 "2147483648"이고, 'x - 65'로 출력해보면 결과는 무려 "2147483520"이 되어 있습니다.


    IEEE의 규격으로 정해진 부동소수점은 기수를 두 개로 한 과학적 기수법으로 표현 됩니다. 이 기수법은 예를 들어 1.d1d2...dp-1 × 2e와 같이 표기됩니다. p는 가수부의 표현에 사용되는 bit수(단정밀도에서는 24, 배정밀도에서는 53)입니다. 두 개의 이웃하고 있는 수의 간격은 21-p+e가 됩니다. 거의 ε|x|와 비슷합니다. ε는 machine epsilon (21-p)을 나타냅니다.


    부동소수점에서 수와 수의 간격이 어떻게 되어 있는지를 정확하게 파악해두면, 연산결과가 예상과 크게 달라져 버리는 사태를 방지할 수 있습니다. 예를 들어 방정식의 탐색과 같이 반복계산을 해야 할 때 정해진 이상의 정밀도를 구하는 코드를 써도 의미가 없다는 뜻입니다. 수와 수의 간격이 너무 넓으면 영원히 답을 구하지 못하고 무한 반복에 빠져버리기 때문입니다.


    부동소수점은 실수의 근사치입니다. 즉 오차가 생기는 건 어쩔수 없다는 뜻입니다. 근사치 처리에 의해 생기는 오차로 인해 연산 결과가 예상과 아예 다른 일도 자주 발생합니다. 예를 들어 거의 같은 크기의 수끼리 연산을 했다고 합시다. 그 경우 최상위 자리는 서로 상쇄되고 최하위 자리에 있던 수(근사치 처리에 의한 오차를 포함한 수)가 최상위의 자리까지 반올림되어 버리는 일이 일어나는 겁니다. 이 연산 결과를 갖고 연산을 거듭해 가면, 오차는 점점 더 커져 갈 수밖에 없습니다. 이런 비참한 결과를 막으려면 자신이 쓴 코드의 알고리즘을 잘 확인할 필요가 있습니다.


    구체적인 예로 방정식 x2 - 100000x + 1 = 0 을, 이차방정식인 근의 공식을 이용해 풀어 봅시다. 이 경우 식 -b + sqrt(b2 - 4)에서 피연산자의 크기는 거의 같으므로 대신에 root r1 = -b + sqrt(b2 - 4)로 계산하면 r2 = 1/r1을 구할 수 있습니다. 이차방정식 ax2 + bx + c = 0의 경우, 근은 반드시 r1r2 = c/a 를 만족합니다. 오차가 커지는 현상은 더이상 눈에 띄지 않게 됩니다. 만일, ex를, 단순하게 1 + x + x2/2 + x3/3! + ...와 같은 방식으로 계산하는 라이브러리가 있다고 가정해봅시다. x가 올바르다면 이 방법에서도 문제는 일어나지 않겠지만, 만일 x가 절대치가 큰 마이너스 수라면 어떻게 될지 생각해 봅시다. 그 경우는 짝수 번째 항의 연산 결과는 큰 양의 수가 되어, 거기서 홀수 항목의 연산결과를 빼게 됩니다. 딱히 큰 오차가 생기는건 아닙니다. 하지만 문제는 짝수 항목의 연산결과에 대한 근사치 처리입니다. 연산결과가 큰 수라면 꽤 상위의 자리로 반올림이 되어 버릴수도 있습니다. 이런 이유로 최종적인 연산 결과는, 양의 무한대로 커지고 원래 얻어야 할 결과와는 크게 달라지게 되는겁니다. 이 문제를 해결하는 방법은 간단합니다. x가 음의 수인 경우는 ex = 1/e|x|을 계산하도록 하면 됩니다.

    마지막으로 금융 관련 애플리케이션에는 부동소수점를 사용해서는 안 된다는 것도 기억해 둡시다. Python이나 C#에 Decimal Type이 준비되어 있는 것은 그 때문입니다.

    부동소수점은 원래 과학기술 계산을 효율적으로 하기 위한 목적으로 만들어졌습니다. 하지만 정확하지 않다면 효율이 아무리 높아도 가치가 없습니다. 부동소수점을 사용할 때는, 어떨 때 오차가 생기는지를 잘 이해하고, 그 지식을 기초로 코딩해야 합니다.


    저자: Chuck Allison

    지금은 유타 밸리 대학 컴퓨터 과학 준교수이지만, 그 이전에는 미국 서부에서 소프트웨어 엔지니어로 약 20여년간 일했다.

    C++ Source의 창시자이고, C++98의 액티브 컨트리뷰터이다.

    "C/C++Users Journal"의 편집장, "Better SofwareMagazine"의 기고편집자이기도 하다.

    Bruce Eckel과 함께 'Thinking in C++. Volume 2.'를 집필했다.


    [프로그래머] 부동소수점은 실수(実数)가 아니다


    [출처] プログラマが知るべき97のこと (O'Reilly Japan)

    이 글은 [CC-by-3.0-US]에 의해 라이센스 되었습니다.




    댓글 0

Designed by Tistory.