![]() |
|
||||||||
경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지 |
|
![]() 오호 제 상식에서도 신기한 일이네요;
그건 C++Builder의 버그가 아닙니다.
전통적인 컴퓨터의 10진수 저장방식에 따른 문제입니다. 본디 컴은 2진수에 근간한 16진수를 내부에서 쓰고 있기 때문에 10진수로 된 실수를 저장하는데 그게 완벽할 수 없습니다. 그래서 약간의 오차가 발생할수 밖에 없습니다. float 타입과 double 타입은 같은 실수 타입이지만 저장공간의 차이에서 float 타입의 오차가 큰 것은 그런 이유 때문이며, 정밀한 계산에서는 double 형도 그 오차를 보일수 밖에 없습니다. 컴이 계산하는 것은 정해진 변수형에 따른 최선의 계산 결과일 뿐입니다. 그래서 말씀하신 그런 오차가 나게 되는 경우가 생기는데, 이는 C/C++ 및 전통적인 컴퓨터 언어의 공통된 문제입니다. 다만 이 이슈가 오래된 것이니 만큼 그리 복잡하지 않은 연산에 있어서는 컴파일러가 알아서 보정해주는 기능이 들어갔느냐 안 들었느냐 또는 그 기능을 쓰느냐 그 기능을 껐느냐 하는 차이가 있을 뿐입니다. 빌더를 만든 개발사가 이 문제를 몰라서 방치한 것은 아니고, 그런 이유가 있기 때문에, 이 문제를 프로그램하는 개발자에게 맡긴 것입니다. 오차가 거의 없이 계산하려면 double 형으로 모두 계산하면 됩니다. 정수형으로 변환하는 int를 넣게 되면 컴파일러는 원칙에 의거하여 뒷부분을 잘라버려 정수 오차가 생기게 되지요. n3 = (int)(1000 * n / 1000.0 ); 이 값은 내부에서 1.99999999999999 식으로 계산되는데 int 로 깍아버리니 1이 되는 것이지요 다른 컴파일러는 이 경우 보정해서 2로 만들어 주는 것이고요. 1000이 실수로 계산되어질때 BCD규칙에 의해 다시 십진수로 꺼내보면 999.999999999999 식으로 되는데 따른 문제입니다. 이를 연산에 사용하면 999.9999999999998 식으로 되는 경우가 생기죠. 이 경우 int로 대입할때는 int는 소숫점 뒷자리가 뭐든지 관계 없니 잘라버리므로 오차가 나게 되지요. 그래서 계산은 double dd = (1000.0 * n / 1000.0 ); int nn = dd; 식으로 실수로 계산하고 그 값을 int 가 건너 받는 식으로 하면 오차를 최대한 줄이게 됩니다. 그런데 이런 보정이 원래 C/C++ 표준이 아니기 때문에, 빌더에서 정수형이 받을 경우 기본적으로 보정이 되지 않게 한 것으로 알고 있습니다. int 와 실수형을 혼용 계산하실때는 컴의 이런 특성을 감안하셔야 합니다. 다른 컴파일러가 보정해 준다고 그걸 또 계속 믿고 순진하게 계산하면 안됩니다. 보정이라는건 한계가 있을수 밖에 없기 때문입니다. 정확한 이유를 알고 계신 분이 있을 것이라 생각했는데, 김태선님이 명쾌한 답변을 주셨네요.
위의 소스에서 컴파일 타임에 계산되는 n1, n2는 보정을 해 주면서, 런타임 때는 보정을 안해 주니까, 개발자 입장에서는 헷갈릴 수 밖에는 없을 것 같습니다. 실수형과 정수형을 혼용하여 사용할 때는, 실수형으로 결과를 받은 후, 정수형으로 옮겨야 겠군요. 정말, 중요한 것을 배운 것 같습니다. 개인적인 생각으로는, C++ Builder 방식이 마음에 들지는 않습니다. 다른 대부분의 컴파일가 보정을 하는 이유는 개발자의 실수를 최대한 줄이기 위한 것 같은데, 개발툴(컴파일러)은 개발자를 위한 것이 아니었던가요? C++Builder 나 델파이 같은 뛰어난 컴파일러를 폄하하는 것은 좀 아닌 것 같군요.
일반 윈도 어플리케이션 개발에 있어서 MS VC나 다른 개발툴에 비해 월등한 성능을 보이는 빌더나 델파이는 그 만한 가치가 있기에 MS의 막대한 물량 공세와 주도권 속에서도 아직 적지 않은 사용자를 확보하고 있는 것이죠. 위에 언급된 연산 문제를 그냥 두는 것은, 제 생각엔 과거 프로그램과의 호환 문제 때문으로 생각됩니다. 새로 만드는 프로그램은 상관 없지만 과거 프로그램 경우는 위와 같은 특성을 이용한 계산이 적지 않게 있기 때문입니다. 그런데 강제로 보정해 버리면 다른 결과가 나오기 때문에, C++ 빌더가 이를 강제 보정하지 않는다고 탓할 문제는 아니라고 봅니다. 최근에 나온 XE 버전의 빠른 CodeCompletion 성능은 놀라운 수준이더군요. 델파이가 한때는 대단히 우수한 컴파일러이자 개발툴 이었는지 몰라도, 현재도 그러할까요?
VCL은 과거에는 위대했으나 이제는 시대의 변화를 따라가지 못하는 구한말 양반같은 처지이고, 컴파일러는 매우 구리고, IDE는 버그투성이이며, 새 버전을 낼때마다 QA는 갈수록 엉망이고 ... 내 생각으로는 위의 저 버그는 버그일뿐 시뿔의 표준과는 무관합니다. 개발자는 편하고 좋은 툴을 사용하면 될 뿐, 저런 사소한 버그를 피하기 위해 머리털을 곤두세워 코딩해야 하는 고단한 존재이어야만 일까요? 개발자가 스스로 불편한 툴을 굳이 사용해야 하는 이유는 없다고 봅니다. 델파이에도 당연히 저런 비슷한 버그가 있습니다. 코드기어 델파이 개발진들의 게을러 터짐 때문에, 전혀 개선이 안된 실수 연산 버그일 뿐입니다. 이와 비슷한 유형으로 델파이만의 은행 방식 반올림이라는 기막힌 버그같지 않는 버그도 있습니다. 자신이 사용하는 툴을 자랑하거나 사랑하는 것은 당연할지 몰라도, 개발자로서 논리적으로 현실을 냉정히 돌이켜 보는 것은 매우 중요하다고 봅니다. 한때 열렬한 델파이 광신도 였던 내가 이런말을 하게 될줄은 몰랐습니다만, 모든 객관적 사실들이 델파이의 앞날이 매우 어두울 것임을 명백히 하고 있습니다. "개발자는 편하고 좋은 툴을 사용하면 될 뿐,"
맞는 말이기도 하지만 틀린 말이기도 합니다. 개발자를 도구 수준으로 바라보는 마음이 있다면 맞는 말이고 자의식을 가진 사람이라고 본다면 틀린 말입니다. 그 생각에 동의하는 개발자도 있지만 동의하지 않는 개발자도 있습니다. 또한 구한말 양반이 몰락한 것은 시대의 흐름을 못 읽은 것 보다 기득권이 부패하고 무능했기 때문입니다. 눈을 뜬 선비들은 무조건 신 문물을 배격하지 않았습니다. 제국주의적 식민지화는 그 이전 산업혁명 등으로 인한 엄청난 물질적 불균형 속에서 태어난 세계사적 비극의 결과물입니다. 지금의 기득권들도 비슷한 양상을 보이고 있지만 정보화로 인해 눈치는 그 때보다 빠를거라 생각되어 약간? 희망은 남아 있어 보입니다만...;; 흠... 그리고 재밌는 테스트를 재연하셨네요. ㅎㅎ 실수와 정수 등의 수치 연산에서 서로 섞어쓰는 경우 Implicit Type Casting이 일어나게되는데 직관적인 입장으로 보자면 이건 QA에 알리는게 좋을것 같습니다. 그리고 타입을 섞어서 연산하는건 자신의 계획 범위를 벗어나는 동작들이(Implicit) 일어날 수 있기 때문에 조심해야 합니다. 저도 연산할 때는 최대한 타입을 맞춰 주고 있습니다. 그리고 지금은 변수에 단순한 값을 가지고 테스트 했지만 실무에서 동작하는 프로그램이 저것보다 복잡한 계산식으로 코딩되어 있다면 어떤 컴파일러라도 프로그래머의 의도와 달리 미묘한 차이를 보일 가능성이 있습니다. 돈 계산 같은 특수한 상황이면 Implicit Type Casting도 생각해서 타입을 맞춰주는 프로그래밍 정도는 해야할 것입니다. 관련 글 리스트
|
Copyright © 1999-2015, borlandforum.com. All right reserved. |