![]() |
|
||||||||
경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지 |
|
물론 개발자 단위의 선호가 있을 수 있지만, 금액같이 정확성이 대단히 중요한 단위에는 가급적이면 부동소수점 값은 쓰지 않는 것이 원칙인데요. 특히 주정섭님은 델파이 개발자이시니 정말 웬만해서는 금액 연산을 위해 부동소수점을 쓰실 일이 없지 않습니까? currency를 쓰면 될 일을. 금액 값은 같은 종류의 금액 값들 사이에선 곱하기나 나누기 등 복잡한 연산보다는 가감 등 간단한 연산 위주이니까 피할 수만 있다면 굳이 부동소수점을 쓸 하등의 이유가 없습니다.
또 C/C++이라고 해도, 금액이라면 int64 등의 타입을 사용하고 소숫점을 따로 취급한 후 나중에 최종 결과 조회시에만 소숫점 처리를 할 수도 있습니다. 방법은 얼마든지 있죠. 금액은 일반적인 부동소수점들처럼 소숫점 밑으로 한정없이 내려가지도 않고 최고값이 한정없이 올라가지도 않는데, 다른 방법이 있기만 하다면 부동소수점을 쓸 이유가 있겠습니까? int max = 1000;
int dif = max - 998; int x = int(1000 * dif / 1000.0); printf("%d\n", x); 이 코드를 여러가지 컴파일러로 컴파일 해보았습니다. vc6 : 2 vc2009 : 2 vc2010 : 2 gcc(mingw) : 2 digital mars c++ : 2 free borland c++ 5 : 1 cbuilder 6 : 1 cbuilder 2010 : 1 cbuilder xe : 1 어떤게 표준에 맞는지는 모르겠지만 사람의 직관도 그렇고 다른컴파일러 모두 2 라고 하는데 홀로 1 이라고 하는건 좀 문제가 있는것 같습니다. 물론 문제가 될수 있는 코드를 작성하지 않는게 우선이긴 하겠지만요. 만일 커런시 타입으로 이문제가 그리 쉽게 해결될 수 있었다면, 터보파워사에서는 뭐 때문에 그런 실수 연산 라이브러리를 만들었겠습니까? 내가 사용하는 공개용 실수 연산 라이브러리 제작자도 상당한 실력자인것은 분명한데, currency 타입을 몰라서 그런 복잡한 라이브러리를 만들었을까요?
나도 저 문제에 봉착했을때 currency도 동원하고 벼라별것을 다 해봤지만, 해결 불가능이었습니다. 이 문제의 근본적 문제는... 1.99999999999... 같은 무리수에 대해서 Trunc를 호출했을 때 이 값이 분명히 2로 반환되어야 하는데, 1로 반환되는 경우입니다. 그래서 유효자리수를 감안해서 실수 오차 보정이 필요한 것입니다. 재수가 좋으면 이런 오차에 봉착하지 않지만, 일단 이런 애매모호한 경우를 당하면 일반적 형변환 방법으로는 속수무책입니다. 연산의 결과가 유리수인 경우, 즉 소수이하가 딱 떨어지는 일반적 정수금액 X 정수수량 인 연산에는 이 오차 발생확률 거의 없습니다. 그러나, 실수금액 X 실수 수량인 경우에는 전혀 상황이 달라지고, 커런시 타입은 소수 이하 4자리 까지가 한계이기 때문에 이런 연산에서 제한될수 밖에 없습니다. 대부분의 금액 계산에 실수 연산 라이브러리를 쓸 필요가 없는데 왜 쓰죠. 아무리 부동소수점 연산이 빨라져도 정수 연산이나 currency같은 고정소수점 타입보다는 느리고 부정확한데요. 또 말씀하신 대로라면 부동소수점 연산 라이브러리의 신뢰도는 절대적이어야 할텐데, 그렇다면 언어 차원이 아니라 CPU 차원에서 지원되어야 마땅하지 않겠습니까?
심성현님, 물론 그런 개발툴들 사이의 차이가 개발자의 편의와 관계가 있을 수는 있습니다. 하지만 앞에서 썼다시피 그게 더 큰 잠재적인 오류를 만들어낼 수도 있습니다. 그래서 컴퓨터의 기본 속성을 벗어나는 보정을 언어 표준으로 하지 않는 겁니다. 컴파일러가 지레짐작으로 보정하는 것이 사람의 머릿속의 지레짐작과 완벽하게 일치할 수가 없기 때문에 예상치 못했던 오류의 가능성이 생기는데, 이런 예상치 못한 오류는 예상할 수 있는 논리적인 오류보다 훨씬 더 심각하죠. 이문제는 보정을 해서 그렇다기 보다는 컴파일러가 SSE2 명령을 사용하지 않아서 발생하는것 같습니다.
SSE2 를 사용하면 VC처럼 작동 하는것 같습니다. (참고) http://en.wikipedia.org/wiki/SSE2 문제 파악을 잘못 하셨다고 봅니다.
애초에 문제가 된 것은 부동소수점이 아니라고 보거든요. 문제가 된 코드를 다시 리뷰해보면, int n1, n2, n3, n4; double d; const int c = 2; int n = 2; n1 = (int)(1000 * 2 / 1000.0 ); n2 = (int)(1000 * c / 1000.0 ); n3 = (int)(1000 * n / 1000.0 ); n4 = (int)(100 * n / 100.0 ); d = 1000 * n / 1000.0; printf("%d, %d, %d, %d, %.0lf", n1, n2, n3, n4 ,d ); 단지 부동소수점의 문제라면 이 코드의 결과가 1, 1, 1, 1, 2 이렇게 나와야 합니다. 그런데 빌더의 결과는 2, 2, 1, 2, 2 이렇게 나오니까요. 언어 차원에서 보정하지 않는게 좋다라고 하셨는데 빌더의 현재 상태는 반은 보정하고 반은 보정하지 않는 어정쩡한 상태라서 n2 과 n3 를 == 연산을 하면 false가 뜨는 기가막히는 상황을 맞을테니까요. 빌더 XE에서는 해결이 되었다니 더 논할 필요는 없다고 봅니다만 문제점은 인식하고 넘어가야겠죠. 김태선님이나 박지훈 님의 글은 저는 충분히 이해하고, 개인적으로 그 내용이 맞다라고 생각합니다.
하지만, 아제나님이 언급하신 것처럼, C++ Builder에서는 일관성이 없다는 것이 개발자에게 혼란을 주는 것입니다. 제생각에는 컴파일 시간에 계산되는 것은 보정이 되고, 실행시간에 계산되는 것은 보정을 하지 않는 것 같습니다. 이 부분에 대해 고수이신 김태선님이나 박지훈님의 의견도 듣고 싶습니다. 저는 C++ Builder 로 99%정도 개발하고, VC++로 1%정도 개발합니다. Turbo-C 때부터 사용했는데, 정말 좋은 C++ 개발 툴이라고 생각합니다. 델파이 때문에 C++ Builder는 조금 찬밥이 된 것은 아쉽지만, 엔바카데로에서 인수한 후 많이 좋아지고 있는 것도 사실인 것 같습니다. 제 생각으로는 C++ Builder가 델파이와 달리 좀더 C++에 특화된 개발툴로 발전되길 희망합니다. 솔직히 지금은 델파이의 C++버전정도로 보여집니다. 아제나님이 일관성이 없다고 하셨는데, 그렇지 않습니다.
const int c = 2; int n = 2; n1 = (int)(1000 * 2 / 1000.0 ); n2 = (int)(1000 * c / 1000.0 ); n3 = (int)(1000 * n / 1000.0 ); n4 = (int)(100 * n / 100.0 ); 에서 c는 상수이므로, 컴파일 할때 컴파일러가 계산한 결과 값을 대입하고 n 은 변수이므로 계산하는 코드를 컴파일러가 만들어 냅니다. 그러닌까, c 가 들어간 계산은 n1 = (int)(2); n2 = (int)(2); 완전히 같은 문장입니다. 이건 아주 오래던부터 있어온 컴파일러의 기본이고 어느 컴파일러 할것 없이 동일 합니다. 빌더는 매우 일관성 있고 전통적으로 ansi 표준을 준수율이 VC 보다 높아 왔습니다. 최근 에디션은 어떤지 모르겠지만. 조금 다르게 말하면
델파이나 C++빌더의 컴파일러가 sse2같은 10년전 나온 cpu 기술이 (그것이 호환성 때문이었든) 컴파일러에 반영이 안된겁니다. 부동 소수는 원래 오차를 가지고 있지만 fpu는 80비트로 처리 하는데 컴파일러는 중간 과정을 64비트 더블에 넣어서 손실이 발생 합니다. 더구나 sse2는 128비트 이고 손실을 막는 여러기능이 있어서 굉장히 높은 정밀도를 제공 합니다. 이부분은 델파이나 빌더의 64비트 버전에서 좀더 심각 해지는데 64비트 버전에서는 커널모드에서 80비트 fpu레지스터를 사용할수 없고 사용자 모드에서도 콘텍스트 스위칭(스레드전환)시 이전 fpu 상태값을 보존하지 않기 때문에 os가 저장과 복원을 반복해야 하는등 여러가지 문제점이 있어서 64비트 버전에서는 아마도 sse2를 사용해야 할 상황입니다. 따라서 확신할 순 없지만 64비트 버전에서는 지금의 VC와 유사한 연산 결과가 나올것이며 기존 32비트 버전과는 다른 부동소수 연산결과가 예상됩니다. 아니면 뭐 성능이고 뭐고 귀차니즘과 호환성 때문에 80비트 fpu만 끝까지 고집 할수도 있겠네요,... 어떻게 될지 제품이 나와바야 알겠지만 ... 관련 글 리스트
|
Copyright © 1999-2015, borlandforum.com. All right reserved. |
금액이 큰 돈을 계산하다보면 이 오차가 때로는 아주 치명적이 됩니다.
델파이라면, 특히 Trunc, Frac, Int, Round 등을 호출할때 빈번해집니다. 재수가 좋으면 이 버그에 안 시달릴수도 있습니다. 빈도는 적지만 전혀 의외의 경우에 이 오차가 발생하는데, 일단 발생하면 일반적인 단순 형변환으로는 해결 불가능합니다.
이런 실수오차 문제를 해결하려면, 큰 금액이나 유효자리수가 큰 수를 계산할때, 이런 실수 연산의 유효자리수 오차를 이해하고 자동으로 보정하는 라이브러리를 사용해야 합니다. 과거 터보파워 제품 중에도 이런 실수 연산 라이브러리가 있고, 공개된 버전의 라이브러리도 있습니다.
그런데, 내 생각으로는 컴파일러 차원에서 이런 뻔한 실수 연산의 유효자리수 오차를 보정해 준다면 개발자 관점에서는 졸라 편하다고 봅니다. 나의 프로그램에서도 큰 금액인 경우, 무조건 이 실수연산 오차 보정 라이브러리 함수를 호출합니다만, 이거 졸라 번거롭습니다.