본문 바로가기
알고리즘 설명

부동소수점 이해하기

by 다빈치코딩 2024. 4. 15.

목차

    반응형

    0.1 + 02 = 0.3?

    파이썬에 대해 공부하면 다음과 같은 이야기를 들을 수 있습니다. 0.1 + 0.2는 0.3이 아니라는 말입니다. 말도 안되는 이야기 같지만 사실 입니다. 다음과 같이 코드를 작성하여 확인해 보시기 바랍니다.

    print(0.1 + 0.2) 
    # 0.30000000000000004
    

    결과는 다음과 같이 0.3보다 미세하게 큰 0.30000000000000004 이 됩니다. 따라서 두 값의 합을 0.3과 비교하는 것도 틀리게 됩니다.

    print(0.1 + 0.2 == 0.3) 
    # False
    

    이런 말도 안되는 일이 발생하는 이유는 컴퓨터는 숫자를 이진법으로 표현하기 때문 입니다.

    실수를 이진수로

    컴퓨터가 숫자를 저장하는 방식을 생각해 보겠습니다. 우리는 이미 컴퓨터가 2진수밖에 인식하지 못한다는 사실을 알 고 있습니다. 10이라는 숫자를 저장할 때에는 10이라고 저장하는 것이 아니라 1010 이라는 이진수로 저장합니다. 일반적인 정수는 이진수로 저장하면 되지만 소수점이 있는 실수들은 이런 방식으로 저장할 수 없습니다.

    한가지 예로 0.625 를 이진수로 변환해 보겠습니다. 정수를 이진수로 변환할 때에는 2로 나누어 계산했다면 소수점 이하의 수를 계산할 때에는 2를 곱해가면서 나타냅니다. 1의 자리가 1이면 1을, 0이면 0으로 나타내면 됩니다.

    • 0.625 * 2 = 1.25 ….. 1
    • 0.25 * 2 = 0.5 ….. 0
    • 0.5 * 2 = 1 ….. 1

    이렇게 계산하여 0.625의 이진수는 0.101(2)이 됩니다.

    그럼 아까 말도 안되는 연산이었던 0.1을 이진수로 바꿔보겠습니다.

    • 0.1 * 2 = 0.2 …. 0
    • 0.2 * 2 = 0.4 …. 0
    • 0.4 * 2 = 0.8 …. 0
    • 0.8 * 2 = 1.6 …. 1
    • 0.6 * 2 = 1.2 …. 1

    즉 0.1을 이진수로 변환하면 0.000110011001100(2) 이 됩니다. 소수점 아래로 계속 반복되는 숫자를 볼 수 있습니다. 우리는 십진수로 단순하게 쓰는 0.1이 이진수로 표현하면 끝이 없는 순환소수가 되어 어쩔수 없이 오차가 생길수밖에 없습니다.

    고정 소수점(fixed point) 방식

    이제 반대로 이진수 0.101(2) 을 십진수로 변환 하는 방법을 알아보겠습니다. 이진수로 변환하기 위해서 2를 곱해주었던 것과 반대로 2로 나누어 주며 계산하면 됩니다. 좀 더 계산하기 쉽게 하기 위해서 2로 나누는 것이 아니라 1/ 2을 곱해주는 형식으로 진행 합니다.

    만약 10.625를 저장한다고 한다면 실수를 정수부와 소수부로 나누어 정수부에는 1010을 저장하고 소수부에는 101을 저장하는 방식이 고정 소수점 방식입니다. 말 그대로 소수점이 시작하는 위치가 고정되어 있어 소수점 위의 숫자는 정수를 나타내고, 소수점 아래는 소수를 나타내는 방식입니다. 고정 소수점 방식은 빠르게 실수를 저장할 수 있고 정확하지만 저장할 수 있는 숫자의 크기가 제한적입니다. 숫자를 저장할 때마다 정수 따로, 소수 따로 저장하려면 큰 수를 저장 하기에는 좋지 않은 방식 입니다.

    부동 소수점(floating point) 방식

    그럼 이제 큰 수를 저장하기 위해서 컴퓨터가 사용하는 방식을 알아보겠습니다. 바로 부동 소수점 이라고 하는 방식입니다. 앞에서 소수점의 위치를 고정했기 때문에 정수부, 소수부로 나뉘어 숫자를 만들다 보니 문제가 있다고 했습니다. 부동 소수점 방식은 가수부(mantissa)와 지수부(exponent)로 나누어 표현하는 방식 입니다.

    실수를 이진수로 바꾸는 수식을 하나 살펴보겠습니다.

    $$ 10.625 = 1010.101_{(2)} = 1.010101_{(2)} \times 2^3 $$

    아까 배웠던 10.625를 저장하는 방식을 표현한 것입니다. 1010과 101을 나누어 저장했던 고정 소수점 방식에서 2의 제곱꼴 형태로 바꿔준 것입니다. 1010.101(2)는 1.010101(2)가 되었습니다. 이 부분이 가수부가 되는 것입니다. 그리고 2^3 이 되는 부분이 지수부 입니다. 이렇게 변경하면 가수부에 숫자를 저장해둔 다음에 지수부에 있는 수로 제곱을 해주면 되기 때문에 고정 소수점 방식보다 큰 수를 저장할 수 있습니다.

    정리

    컴퓨터는 실수를 저장할 때 부동소수점이라는 것을 사용해서 저장하기 때문에 우리가 계산한 것과 오차가 발생 합니다. 부동 소수점으로 어떻게 변화되고, 오차가 얼마나 발생하는지 알 필요는 없습니다. 우리가 기억해야 할 것은 컴퓨터에서 실수를 연산할 때에는 어쩔수 없이 오차가 발생한다는 사실을 인지하는 것입니다.

    지금은 정확한 계산을 위해 많은 방법이 나와있어 부동 소수점 오차를 쉽게 해결할 수 있습니다. 십진수로 표현법을 만든 decimal 모듈을 사용하거나, 유리수 기반으로 되어 있는 fractions 모듈을 사용하면 됩니다. 정말 실수 연산을 많이 사용한다면 Numpy(Numerical Python) 패키지를 사용하는 것도 한가지 방법 입니다. 부동 소수점 오차를 인지하고 있다가 0.1 + 0.2 같은 문제가 발생했을 때 당황하지 않고 다른 해결 방안을 실행에 옮기면 됩니다.

    반응형

    '알고리즘 설명' 카테고리의 다른 글

    등차수열과 등비수열  (0) 2024.03.28
    교란 순열이란?  (0) 2024.03.22
    한 붓 그리기 가능한 경우  (0) 2024.03.15
    파이썬 음수의 나머지 연산  (0) 2024.03.11
    2차원 배열 회전하기  (0) 2024.01.24