파이썬 부동 소수점(Floating Point) 문제(Issue) 파악

KLDP에서 파이썬의 부동 소수점과 관련한 스레드가 올라왔다: http://kldp.org/node/132646
그래서 이에 대해서 찾아보다가 얻게 된 결론이다.

>>> (0.05 + 0.05 + 0.05).hex()
'0x1.3333333333334p-3'
>>> (0.15).hex()
'0x1.3333333333333p-3'
>>> float.fromhex('0x0.0000000000001p-3')
2.7755575615628914e-17
>>> Decimal(0.15) - Decimal(0.05 + 0.05 + 0.05)
Decimal('-2.775557561562891351059079170E-17')
1번 실행은 0.05를 세 번 더한 hex 값 표현을 나타낸다. 2번 실행은 0.15 자체의 hex 값을 나타낸다. 그런데 두 hex 값의 오차가 발생한다.

이를 직접 빼서 다시 부동 소수점으로 환산해보면 그 오차를 알 수 있다.

그리고 파이썬에서 Decimal 모듈을 지원하는데, 이 모듈은 정확한 소수 연산을 수행해준다. 즉, Decimal(5)/Decimal(100)과 같이 쓰면 정확히 5/100 을 나타내고, 이를 20번 빼면 정확히 0과 같이 나타내준다.

그래서 이를 사용해서 빼보면 4번째 실행이 나온다. 즉, hex 의 결과와 같은 것을 알 수 있다. 따라서 오차 계산이 정확히 된 것을 알 수 있다.

그리고 0.1 - (0.05 + 0.05)의 경우 0으로 정확히 나오는데, 아래와 같이 hex 값이 같기 때문이다.
>>> 0.1.hex()
'0x1.999999999999ap-4'
>>> (0.05 + 0.05).hex()
'0x1.999999999999ap-4'
>>> Decimal(0.1) - Decimal(0.05 + 0.05)
Decimal('0E-55')

또, 추가적으로 0.15 - 0.05 - 0.05 - 0.05 와 0.15 - (0.05 + 0.05 + 0.05)의 오차 결과는 또 다르다!
>>> 0.15 - 0.05 - 0.05 - 0.05
-1.3877787807814457e-17
>>> 0.15 - (0.05 + 0.05 + 0.05)
-2.7755575615628914e-17

따라서 스레드의 질문을 정확히 분석하자면 0.05의 hex 값을 1.0의 hex 값에서 모두 뺌으로써 마지막 결과의 이유를 알 수 있을 것이다.
그리고 0.05 - 0.05의 경우 같은 hex 값을 갖는 수를 빼는 것이므로 당연히 0이 나오게 될 것이다.

참고자료:
http://docs.python.org/tutorial/floatingpoint.html
http://docs.python.org/library/stdtypes.html#float.hex

댓글 없음:

댓글 쓰기