Post

[Computer Science] 소수점의 배신

0. 들어가기전에


여러분은 두수를 비교하기 위하여 어떻게 하시나요?
부등호를 이용해 비교하시나요?
그러면 한번쯤 다음과 같은 오류를 겪어보셨을겁니다.

1
2
3
0.1 + 0.2 == 0.3

# False

이는 상식적으로 이해가 되지않습니다. 이를 이해하기 위해 이번 포스팅을 시작하겠습니다.

1. 비트와 바이트


흔히 정보는 0과 1로 이루어져있다고합니다.
이는 컴퓨터가 인식할 수 있는 것이 0과 1이기때문입니다.

1.1) 비트

  • 데이터를 표현할 수 있는 가장 작은 단위입니다(0과 1)

1.2) 바이트

  • 8개의 비트가모이면 1개의 바이트가 됩니다. 컴퓨터 메모리가 파일을 측정할 수 있는 가장 기본적인 단위입니다.


2. 정수처리


2.1) 양수가 컴퓨터에 저장되는 방법

10진수를 2진수로 바꾸면 됩니다.
예를들어 숫자 5는 다음과 같이 표현됩니다

0
0
0
0
0
1
0
1

2.2) 음수가 컴퓨터에 저장되는 방법

음수를 저장하려면 2의 보수로 변환하는 추가 과정이 포함되며 과정은 아래와 같습니다.

  • STEP 1 ) 2진수로 변환
0
0
0
0
0
1
0
1


  • STEP 2 ) 1의 보수(각 비트를 반전시켜줍니다.)
1
1
1
1
1
0
1
0


  • STEP 3 ) 2의 보수(1을 더해줌)
1
1
1
1
1
0
1
0
+
0
0
0
0
0
0
0
1
=
1
1
1
1
1
0
1
1

2.3) 컴퓨터가 양수, 음수를 구별하는 방법

가장 앞의 자리의 비트를 비교해서 양수와 음수를 구별하며 이를 부호비트 라고 합니다.
그러면 129를 8비트로 표현하면 다음과 같다고 생각하실겁니다.

1
0
0
0
0
0
0
1


하지만 이는 틀렸습니다.
가장 앞의 자리수는 부호비트이므로 컴퓨터는 이 숫자를 -1이라고 인식할것입니다.
따라서 128은 8비트에 저장하지못하고 16비트에 저장해야되서 다음과 같이 저장이 됩니다.

0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0


이제 -128을 저장하면 가장 앞의 부호비트가 1이니까 다음과 같습니다.

1
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0


3. 소수처리


소수를 저장하기 위해서는 컴퓨터가 소수를 인식하기위해서 사회적 합의를 본 규칙을 이해해야하며 이 규칙은 IEEE에서 만들어진 표준을 사용합니다.
이를 3.375를 저장하는 과정을 통해 보여드리겠습니다.

  • step 1 ) 부호비트(Sign bit)
    • 양수이므로 0
0


  • step 2 ) 정규화(Exponent)
    • 정수부분 3(10) = 11(2)
    • 소수부분 0.375(10) = 0.011(2)
    • 2진수 변경 결과 : 3.375(10) = 11.011(2)

    • IEEE 754 부동소수점 형식에서는 소수점은 항상 첫번째 1 뒤에 오도록 해야합니다.
    • 결과 : 11.011 = 1.1011 x 21

    • IEEE는 Bias라는것을 사용하며 단정도에서는 Bias가 127이므로 실제 지수값은 127을 더한 후 2진수로 변환합니다.
    • 지수부분 결과 : 127 + 1 = 128 = 10000000(2)
0
1
0
0
0
0
0
0
0


  • step 3 ) 가수(Mantissa)
    • 정규화된 이진수에서 소수점 이하의 숫자만을 사용합니다.
    • 가수비트는 23비트로 저장되며 나머지는 0으로 채웁니다.
    • 결과 : 0110000000000000000000
0
1
0
0
0
0
0
0
0
0
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

4. 순환소수 문제


여기서 소수점을 2진법으로 바꿀때 문제가 생깁니다.
0.1을 2진법으로 바꾸면 0.1(10) = 0.000110011001100110011…(12)와 같이 0011이 계속 반복되며 이를 순환소수라고 합니다.
이를 IEEE를 통해서 바꾸면 다음과 같이 일부 숫자가 누락됩니다.

0
0
1
1
1
1
0
1
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
0
1
1
0
.
.
.
.
.

이렇게 일부 누락된 숫자만큼의 오차가 발생하게 됩니다. 그래서 아래 코드는 True를 반환하게됩니다.

1
2
3
0.1 + 0.2 > 0.3

# True


3. 해결방법


  1. 소수를 연산을 피합니다.
    • 소수를 지양하면서 0.01계산이 필요하면 x100을 하여 1로 계산하는등의 노력이 필요합니다.
  2. 일정수치 이하의 오차는 허용합니다.
    • Epsilon와 같은 일정 수치 이하의 오차는 허용하는 방식이 있습니다.
  3. 정밀도 함수를 사용합니다.
    • Decimal같은 함수를 사용하여 소수를 정확하게 나타냅니다.
    • 하지만 이는 어쩔 수 없는 오차를 포함합니다.
  4. 분수를 통하여 계산합니다.
    • 소수점말고 분수로 계산을하면 매우 효과적으로 해결할 수 있으나 사용에 제약이 많이 따릅니다.

이외에도 다양한 해결방법이 존재합니다.
이를 고민하고 본인의 상황에 맞는 방법을 채택하여 사용하면됩니다.


글을 마무리하며


이번에는 많은 초보 코더들이 실수할 수 있는 순환소수문제를 분석해보았습니다.
실제로 이를 이해하고 메모리구조에대해 접근하는 시각을 가질 수 있으면 추후 다양한 문제상황을 진면할때 보다 손쉽게 이해하고 해결 할 수 있을겁니다.
혹시 궁금한점 혹은 잘못된점이 있으면 언제라도 댓글주시면 확인하도록하겠습니다!

이만 가보겠습니다!

This post is licensed under CC BY 4.0 by the author.