zungwooker   2년 전

여러 언어에서 0.1 + 0.2 != 0.3 이라는 사실은 익히 알고 있는 사실이며

이는 0.1, 0.2, 0.3 을 완벽하게 표현할 수 없고 어림 값으로 표현하기에 생기는 오차가 원인으로 알고 있습니다.

부동소수점을 표현하는 방법인 IEEE 754에 관한 질문이 있습니다.

1.

0.1 (10) = 1.100110011001...*2^-4 (2)

0.2 (10) = 1.100110011001...*2^-3 (2) 으로 표현할 수 있으며

두 수의 합은

0.3 (10) = 1.001100110011..*2^-2 (2) 입니다. (제가 계산하였는데 틀렸다면 알려주세요)

배정도의 가수부는 52bit로 0.3을 배정도로 표현하였을 때 가수부는 "001100110011...0011"이라 생각하여

이로 계산해보니 0.3보다 작은 0.29999..가 되고

마지막 bit에서 반올림해주니 통용적으로 사용되는 값이 도출되었습니다.

왜 마지막에 반올림을 해주는 것인가요?

반올림을 해주는 기준이 무엇인가요?

어디까지가 유효숫자인가요?

2.

0.1과 0.2, 0.3은 모두 어림 값인데 

합의 결과는 오차가 생기고 특정 변수에 초기화(num = 0.3) 같은 결과는 오차가 생기지 않는 이유가 무엇인가요?

3.

Python에서 type(0.3)은 float인데 배정도를 사용하는 이유는 무엇인가요?

긴 글 읽어주셔서 감사합니다.

오늘도 좋은 하루 보내세요.

djm03178   2년 전

1. 마지막 비트가 아니라 그 아래 비트에서 반올림을 한 거라고 생각됩니다. 일반적인 반올림하고 똑같습니다. 반 이상일 때 반올림을 해야 실제 값에 더 가까워진다는 논리입니다.

2. 둘 다 오차가 발생합니다. num = 0.3은 안 생긴다고 생각하신 이유가 있나요? https://ideone.com/vPGNrY

3. Python에서 말하는 float는 그냥 부동소수점의 영어 단어인 floating point를 의미할 뿐 단정도랑은 전혀 상관이 없는 단어입니다. 그냥 C/C++에서 float라는 단어를 단정도를 위해 사용하고 있을 뿐입니다.

zungwooker   2년 전

1.

실제로 0.1과 0.2를 더하는 과정을 다음과 같이 표현한다면

     1.1001 1001 1001 ... 1001 * (2^-3) -> 0.2

+   0.1100 1100 1100 ... 1100 * (2^-3) -> 0.1

-----------------------------------

   10.0110 0110 0110 ... 0101 * (2^-3)

= 1.0011 0011 0011 ... 00101 * (2^-2) (가수부 53bit)

이므로 배정도에서 가수부는 52bit를 사용하기 때문에 마지막 53번째 비트를 반올림하면

1.0011 0011 0011 ... 0011 이 됩니다.

이를 계산하면 0.2999999999999999... 이므로

0.30000000000000004 와는 분명 다르게 결과가 나옵니다.

그런데 0.1과 0.2를 더하여 반올림한 결과인 1.0011 0011 0011 ... 0011 에서

한번 더 반올림을 하여 1.0011 0011 0011 ... 0100 으로 계산하면

0.30000000000000004... 이 도출됩니다.

결론적으로 말씀드리면

이미 덧셈 과정에서 반올림을 해주었는데 또 다시 1bit를 더해주는 과정이 필요해 보입니다

왜 이런 현상이 일어나는(필요한) 건가요?

2.

보내주신 코드 살펴보았습니다. 감사합니다.

제가 궁금했던 부분은 

파이썬을 예로 들면

>>> num = 0.1 + 0.2

>>> num

0.30000000000000004

>>> num2 = 0.1

>>> num2

0.1

0.3과 0.1 모두 어림 값으로 저장하여 오차가 생기는 값인데도 불구하고

덧셈이라는 과정이 생기면 비교적 작은 오차까지 유효숫자로 판별하고

산술 과정이 없이 할당한 값을 참조하면 이전과 같이 오차를 무시하고 0.1이 보여지는 듯 합니다.

0.1을 할당하여 후에 불러오더라도 이는 0.1과 0.2를 더한 결과와 동일하게 유사 값이니

0.30000000000000004과 같이 오차까지 출력되어야 하는 것이 아닌가요?

이 부분이 이해가 되지 않습니다.

보내주신 코드에서도 왜 덧셈의 결과와 할당된 결과 값이 다른지 이해가 가지 않습니다.

알고 계신 부분이 있다면 공유해주시거나 알려 주실 수 있으신가요?

도움을 주셔서 정말 감사합니다.

djm03178   2년 전

1. 처음 0.2와 0.1을 표현하실 때부터 반올림이 되어야 합니다. 0.2를 1001로 끝난다고 하셨는데, 실제 값은 이 다음 비트가 1이므로 여기서 이미 반올림 처리를 해야 하므로 저장되는 값은 1010입니다. 마찬가지로 0.1의 표현도 0.2와 동일합니다. 이렇게 놓고 더해보시면 따로 반올림 처리를 하지 않아도 0.30000000000000004...가 나오는 것을 보실 수 있습니다.

2. 그건 오차가 무시된 것이 아니고, 0.1 + 0.2라는 덧셈 결과가 만들어 낸 수가 0.3에서 가장 가까운 수가 아니기 때문에 발생하는 현상이라고 생각됩니다. 0.3을 그대로 대입하면 저장되는 수는 0.2999...8890인데 비해, 0.1 + 0.2의 결과는 0.3000...4441로 0.3과의 차이가 더 큽니다. 정확한 구현체의 동작 방식은 모르겠으나, 제 추측으로는 해당 값이 어떤 '간단한' 소수를 나타낼 때 실제로 저장되는 값과 동일하다면 더 간단한 표기를 보여주는 것 같습니다. 즉, 0.3이라고 쓴 것이 실제로는 0.2999...8890으로 저장되기 때문에 이런 오차가 있는 값이더라도 0.3과 동일한 것으로 보고 출력해주고, 0.3000...4441은 0.3을 나타내려고 할 때 저장되는 값이 아니므로 0.3과는 다른 수로 보고 0.3000...4를 보여주는 거라고 생각됩니다.

확인해보고 싶으시다면 아래와 같은 걸 테스트 해보시면 이것도 똑같이 0.3을 출력하는 것을 보실 수 있습니다. 0.1 + 0.2에서 0.000...3을 뺀 것도 여전히 오차가 있겠으나, 그냥 0.3을 저장할 때와 동일한 값이 저장되어있기 때문에 똑같이 0.3을 출력해줍니다.

zungwooker   2년 전

궁금한 점이 시원하게 해결이 되었습니다.

정말 감사합니다.

좋은 저녁시간 되세요.

댓글을 작성하려면 로그인해야 합니다.