gcc가 자신이 돌았다고 고백했습니다.
글쓴이: seoleda / 작성시간: 월, 2006/04/24 - 2:35오후
double sum; sum = 0.3+0.6+0.1; //sum = 0.3f+0.6f+0.1f; printf("%f\n", sum); if (sum==1.0){ printf("I am smart!!\n"); }else{ printf("what!!, am I creazy?"); }
gcc가 자신이 돌았다고 고백했습니다.
sum을 float로 하면 결과가 제데로 나오긴 하지만, 위와 같은 코드는 예상한대로 동작하지 않더군요.
gcc 버젼이 3.3.5-13 인데, gcc 버그인가요?
방금 ubuntu/breezy (gcc 4.0.2) 에서도 자신이 돌았다는 고백을 받았습니다. 어찌해야 하나요?
점점더 해깔려집니다. 내가 돌은건지 컴퓨터가 돌은건지..
혹시 몰래 카메라가 아닐까 의심도 듭니다. TT
Forums:
일단은 asm 컴파일 된 것을 보셔야 할 것 같은데 올려주세요~
asm 으로 컴파일 된 것을 봐야할것 같네요.^^;
플로팅 포인트 비교...
플로팅포인트 변수의 == 비교는 좋지 않습니다.
fraction을 위한 비트가 한정되어 있기 때문에... 어쨋든 모든 실수를 정확하게 표현할 수가 없죠. 0.1의 경우에도 1/2 + 1/2^2 + 1/2^3 ...으로만 표현되지 정확하게 나타낼 수가 없으니까요.
컴퓨터가 실수를
컴퓨터가 실수를 표현하는 방법상의 오차 문제입니다. 실수의 값 비교는 등호로 하지 않는 것이 문제를 피해가는 방법입니다. (그렇게 하면 안되는 것입니다.) 오차범위를 검사하여 그 범위에 들면 맞다고 판단하는 것이 맞습니다. 그 오차범위는 실수값의 정밀도나 연산의 결과에 따른 것으로 하는 것이 일반적입니다.
여기 게시판에도 몇번 언급된 문제이므로, 검색을 해보시면 여러가지 의견이 있을 것입니다.
GCC가 잘못된 것이
GCC가 잘못된 것이 아니라, 컴퓨터에서 floating point(실수)를 표현하는 방식때문에 그렇습니다. 사실 정확한 실수를 표현하기 위해 특별한 실수 처리 라이브러리를 쓰지 않는한 모든 실수를 다 표현하는 것은 불가능합니다.
간단히 예로 들어 2진수 기반에서 십진수 0.1도 제대로 표현할 수 없습니다. (이진수 0.0001100110011...로 표현됨).
printf(3)가 보통 이런 수치를 만나면 반올림해서 보여주기 때문에, 실제 표현 값과 다르게 보일 수 있습니다. 즉 sum이 1로 출력되더라도 사실 1이 아닌 0.99999... 일 가능성이 있습니다. 따라서 sum == 1.0이 실패할 수도 있습니다. 두 실수를 비교할 때, 이런 식으로 하는 것은 오차가 있을 수 있기 때문에 적당한 방법이 아닙니다. 차라리 다음과 같이 어느 정도 오차를 인정하는 선에서 코드를 작성해야 합니다. 만약 두 실수 a, b를 비교한다면,
를 쓰는 것은 나쁜 방법이며,
로 쓰는 것이 좋습니다. 이 때 EPSILON은 data type이 float, double, long double일 때, 각각 FLT_EPSILON, DBL_EPSILON, LDBL_EPSION을 쓰는 것이 좋습니다.
epsilon의 정확한 의미나 더 자세한 것은 수학을 잘 하시는 분들이 해 주실 겁니다. 전 수학에 약해서... :(
--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
옆사람에게 물어
옆사람에게 물어 봤더니, 왜 그런지는 잘 모르지만
if (a-b <=0) {...}
로 하라고 하더군요. b가 음수면 어쩔거냐고 물었더니 모른다네요. ^^cinsk님의 방법은 음수일 경우에도 문제가 없을듯 합니다. 감사합니다.
지금 생각해 보니, 소수부가 2^- n 의 합으로 표시되지 않는 경우, 오차가 생긴다는 사실을 수업시간에 들을땐, "나랑 별 상관없는 이야기군" 넘어갔는데 알고보니 상관이 아주 많은 이야기 였네요.
p.s. 좀 다른 이야기지만, C/C++ 의 표준에서 실수형 == 연산을 cinsk 님의 방법과 같이 정의하지 않는 이유는 무었인가요? 혹시 저같이 순진한 사람들을 놀리려는 의도인지... ^^
어쨌거나 틀린 거를
어쨌거나 틀린 거를 맞다고 할 수는 없는 일이지요. 수치 연산을 대단히 많이 하는 프로그램인 경우 이런 오차도 신경을 써줘야 합니다. 오차에 오차가 겹쳐 엉뚱한 결과가 나올 수도 있지요. 이런 경우 가능한한 오차를 줄일 수 있는 계산식을 사용하거나, 속도가 문제되지 않는다면 무한 정밀도 라이브러리를 사용해야 합니다. 그런데 컴파일러 차원에서 그냥 부동소수점 오차를 무시해버리면 이게 오차가 얼마나 생기는 건지, 맞는 건지 틀린건지를 판단하기가 어렵지요. 그냥 한계를 인정한 정직한 정의라고 보시면 됩니다.
ㄹㄹㄹ 님의 의견은
ㄹㄹㄹ 님의 의견은 잘 들었습니다. 하지만, 두 수가 오차범위내에 있을 경우 같다고 판단할 경우, 과연 계산식에서 얼마나 오차가 누적될런지는 잘 모르겠습니다.
계산과정의 오차를 무시해 버리자는게 아니라, 단순히 < em>동등비교연산에 한해서 단순한 비트열을 비교하는 것이 아니라, 오차를 가만한 친절한 연산을 제공했으면 더 좋을것 같아서 얘기를 꺼내봤습니다. 잠깐 ANSI/ISO C standard 문서를 찾아 봤는데, 실수의 동등비교연산자를 어떻게 수행 하라는 얘기는 없는것 같습니다.
여러분은 이 두수는 비트열이 같으므로 같다. (실제로 내부에서 이런식으로 비교 하는지는 잘 모르겠습니다), 이 두수는 오차범위내에 있으므로 같다. 중 어떤것이 더 마음에 드시는지요? 저는 후자가 더 마음에 듭니다. 이유는
컴파일러에서 전자와 같은 행위는
1. floating point는 오차가 있다는 사실을 모르는 사람에게는 혼란을 일으킬 만한 행동이며,
2. 위 사실을 아는 사람에게는, 아 이 두 수는 오차범위내에서 같구나 라는 사실을 유추 할 수 있기 때문에 입니다.
뭐 엄연히 다른 수를 같다고 할 수 있느냐? 라는 의견에는 할말이 없습니다만, 여러분들의 고견을 듣고 싶습니다.^^
p.s. 그런데, 한계를 인정한 정직한 정의라는 표현은 참 마음에 듭니다.
거기까지 컴파일러가
거기까지 컴파일러가 알아서 해줘버리면 실제로 발생하는 오차를 측정할 방법은 전혀 없습니다. 사실 컴파일러 차원에서도 얼마나 오차가 발생하여야지 같다고 정의 할것인가도 모호하구요..
실 예로 3차원공간에 물체가 있다고 합시다.. 여기에서 0.001, 0.003, 0.002 에 있는 점과 0.0011, 0.0031, 0.0021 에 있는 점은 엄연히 위치가 다른 곳입니다. 이런 오차 허용범위를 정의하고 조절해야 하는 자는 해당 코드를 구현하는 구현자이지 컴파일러가 임의적으로 하긴 어려운 범위입니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.
순진한 사람을 놀리려면...
다른 방법을 택했지 않았을련지요...
a가 0일때도 생각해 주세요....
몫은 cinsk님에게..
저는 졸려서...==333
God said it. I believe it. That settles it.
여친이 길르는 용..
우리가 수학시간에서
우리가 수학시간에서 배우던 수와 컴퓨터에서 수를 표현하는 자료형은 엄연히 다릅니다. 보통 특별한 언급이 없으면 수학에서는 무한한 집합을 대상으로 처리하지만, 컴퓨터의 자료형은 유한한 집합을 대상으로 합니다. 그래서 오버플로우니 언더플로우가 등장하는 것이겠죠.
다들 아시겠지만, 정수를 컴퓨터에서 표현한 것은 보통 integer라는 자료형이고, 실수는 float이라는 자료형이죠. 문제는 완벽하게 표현할 수 없다는 것이죠. integer나 float에서 최대값과 최소값이 존재하는 것은 모두들 알고 있습니다. 그런데 float에서 정밀도가 있다는 것은 가끔 잊어버리는 분들이 계시더군요. 수학에서 실수는 연속성을 가지지만, float 자료형은 이가 빠진 톱니처럼 아주(?) 띄엄띄엄 존재합니다. 따라서 최대값과 최소값 사이에도 표현할 수 없는 수가 무한히 많이 존재합니다. 이것들에 대해서 고려해야 하겠죠.
이것은 단순히 float 자료형을 처리하는 방법으로는 정확한 처리가 불가능합니다. 하드웨어적인 한계이기 때문입니다. 만약에 프로그램에서 사람이 분수나 제곱근을 처리하듯히 연산하고 그렇게 표현한다면 가능할 듯 싶습니다. 그러나 속도가 많이 느려지겠죠.(이렇게 속도를 희생하면서 정밀도가 필요한 분야가 얼마나 있을지 모르겠습니다.) 일반적으로는 위에서 cinsk님이 제시한 방법으로 하는 것이 제일 좋을 듯 싶습니다. 더 빠는 속도를 원한다면, float 대신에 int로 하고 출력할 때 적절하게 변환해도 되겠죠.
내 블로그: http://unipro.tistory.com
그런 연산이 필요한
그런 연산이 필요한 분야가 아주 아주 많이 있습니다. 그래서 maple, mathematica 같은 computer algebra system 에는 말씀하신 것처럼 최대한 symbolic computation 을 한 후 마지막에 numerical computation 을 하는 기능이 있습니다. 일종의 lazy evaluation 이지요. 그리고 하드웨어에서 지원하는 부동소수점형 이외에 무한 정밀도로 사용할 수 있는 수 타입을 제공하구요. 무한 정밀도 자료형은 꼭 computer algebra system 이 아니더라도 많은 언어에 기본 형 또는 기본 라이브러리로 포함되어 있습니다. 필요한 데가 많이 있기 때문이지요.
흠...
creazy -> crazy
=3=3=3
컴퓨터 아키텍쳐를 보시면.
프로그래밍 하시는 분이면 컴퓨터 아키텍쳐 중에서
정수 표현법인 2의 보수법과 float 이나 double 표현법인 IEEE754 정도는 숙지하고 계셔야 합니다.
2의 보수법을 숙지 하지 못하면, int i = -100 일때
"i * 2" 와 "i << 1"
이 동일할꺼라고 짐작 것과 seoleda님의 질문은 컴퓨터의 내부적으로 숫자를 어떻게 저장하고 핸들링을 하지는 모르기 때문에 생기는 문제이지요.소수점부분의 오차 문제는
수학적으로 이야기를 하자면
모든 10진수 정수는 2진수로 표현이 가능하지만
10진수 소수는 간혹 2진수로 표현할때 10진수로는 유한소수인데도 불구하고
2진수에서는 무한소수가 되는 경우가 있습니다.
반대로 1/3 같은경우 10진수에서는 무한소수이지만 3진수에서는 유한소수가 되지요.
다시 컴퓨터로 넘어와서
위의 무한소수나 혹은 매우 많은 자릿수를 갖는 소수는 컴퓨터의 자료구조에 담을 수가 없을 것이라는 것은 쉽게 아실 듯 하구요.
그래서 자료를 담을수 있는 한계점에서 올림 혹은 내림, 반올림을 발생하게 됩니다. 그때 생기는 오차가 결과적으로 우리가 보는 오차 이지요.
위에서도 언급됐지만
왜 글을 입력하면 중간에 잘리죠... -_-;;;
Orion Project : http://orionids.org
위에서도 언급됐지만
위에서도 언급됐지만 cinsk 님께서 제시하신 식은 a 가 0 일 때 문제가 있습니다.
제가 보아온 수치해석 루틴들은 EPS 값을 인자로 받는 경우도 있고, 고정값으로 적용하는 경우도 있습니다만, 대개는 위의 식처럼 비교값에 따라 스케일하지는 않습니다.
저는 다음과 같은 함수를 정의해서 사용합니다. 즉 -eps ~ +eps 사이 값은 0으로 처리하는 것입니다. 두 값을 비교하려면 두 값의 차를 입력하면 되겠죠.
Orion Project : http://orionids.org
댓글 달기