C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
분야별 포럼
C++빌더
델파이
파이어몽키
C/C++
프리파스칼
파이어버드
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

자유게시판
세상 살아가는 이야기들을 나누는 사랑방입니다.
[18018] C++Builder 6.0에서 double 좀 이상합니다.
박영목.월천 [gsbsoft] 5321 읽음    2010-04-05 07:12
이것 뭐 회사 다니는 것도 아닌 데...    점점  더  바빠지는 것  같습니다.
사람이 좀 편하게 살아야 하는 데...  또 일만 하나 웬만하면.....
남는 시간은 가족과 보내려고 노력합니다.  부자들은 그렇게 생활하더군요

엔지니어들이 가난한 이유가 일을 많이 해서 그런 것이 아닌가? 생각합니다.
일만 하니... 협상도 안되고(피곤하고 기가 약해서)... 다 귀찮아 계약도 어쩡어쩡...
개발비를 떼먹어도 그 다음 일이 바빠... 어쩡어쩡 넘어가고 그러다 보니 배가 고프고
배가 고프다 보니 다른 계약도 액수가 적어도 그냥 또 계약을 하고 계속 악순환의 연속
인 것이 아닌가?  --- "건강하지 않는 사람은 수익도 적다"
일은 적게 하고 다른 부수적인 것에 머리를 굴리는 것이 수입에 있어서 더 효율적이라
생각합니다. 회사에 소속해 있어도 일만 한다면... 비슷한 결과가 나오지 않을까?

성경에 "자기 가족을 돌아보지 않는 자는 불신자보다 더 악한 자니라" 이런 말씀도 있고...
물론 설교야 가족을 전도해서 구원하지 않는 자 이런 식으로 해석할 수 있겠지요
아니면 생활비를 주지 않고 사람들과 술이나 쳐먹는 사람.... 

저는 가족과 보내지 않는 사람....  가장 악한 놈이라 생각합니다... 그래서 왠만하면 남는
시간은 가족에게 사용하려고 합니다...

요즘 술을 마시자는 사람이 많아서 아~~ 피곤합니다. 이것 2~3일 한번 꼴로...  전화가 오니...
술도 좋아하지 않는 데...  그렇다고 거절도 잘 하지 못하는 편이라 나가지만 아주 피곤합니다.
했던 얘기 또하고 또하고...  이렇게 되면 다음 날도 일도 안되고... 그래서... 이제는 사업상
아니고는 다 끊어려고 합니다.  이제 저도 나를 볼봐야 하겠다는....  위기 의식 때문에...

이 밤도 웬만하면 일찍 자고 가족들에게 웃음을 주어야 하는 데... 코딩으로 고민한다고...
또 밤을 보내어 버렸네요...  혹 오늘 가족에게 짜증을 내지 않을까 걱정입니다.

오늘 새벽에 고민은 조금 더 정밀하게 값을 넣기 위해 3.175 마지막 5를 반올림 하려고 시도하다
가 뭔가 이상하게 계산에 착오가 발생하는 것입니다. 계산기로 하면 정확한 데... 뭐야  이렇게
해서 하루밤을 다 보내고 말았습니다. 날짜 맞추려면 잠자는 시간 외에는 시간이 없는 데....
술도 아니고 사람도 아니고... 코딩까지... 나를 애를 먹이는 군요...

그래서 VC++에서 테스트 해보았습니다.  어... 여기서 왜 되지...????


//VC++ 6.0
void CRrrDlg::OnButton1()
{
  // TODO: Add your control notification handler code here

  float  fData   = 3.175;
  double dlData  = 3.175;

  int    valueF  = fData *1000;
  int    valueD  = dlData*1000;


  char str[100];

  wsprintf( str, "%d, %d", valueF,  valueD );

  ::MessageBox( this->m_hWnd, str, "MSG", MB_OK );

  결과: 3174, 3175
}

//C++Builder 6.0
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  float  fData   = 3.175;
  double dlData  = 3.175;

  int    valueF  = fData *1000;
  int    valueD  = dlData*1000;


  char str[100];

  wsprintf( str, "%d, %d", valueF,  valueD );

  ::MessageBox( this->Handle, str, "MSG", MB_OK );

  결과: 3174, 3174
}
//---------------------------------------------------------------------------


double에서 했는 데... 값이 틀려서... 찍어보니...  VC와 다루군요......
그러고 보니... 8년 전인가? 전자칠판 핵심 BSpline 곡선 부분 처리 부분...
VC로 만들어 델파이로 만들어 주었는 데... 그때도 소수점에서... 뭔가...
이상해서... 억지로 보정을 한 기억이 있군요.  이번에는 그런 보정이 안되는 데...
아 피곤합니다.   왜 이런 결과가 나오는지....?? 아시는 분 답변 부탁드립니다.

이 글을 적다가  생각이 나서 double에 1000을 곱해서 double에 넣어 int에 넣어니...
호호호  답이 맞게 나오네요...   이것 뭐야....

  double dlData  = 3.175;
  dlData *= 1000;
  int    valueD  = dlData;

  ShowMessage( valueD );    //3175

그래서     int    valueD  = (dlData*1000);  괄호를 쳐보았습니다. 안되는 군요...  Compiler Bug인가?
update 4번도 했는 데....    int valueD = (double)(dlData * 1000); 이것도 안되네...  dlData * 1000  여기서
값이 벌써 정해지는 것으로 보이네요... 

아시는 분...   답변 부탁드립니다. C++Builder, Delphi 다른 버전에서도 그런 것인지...???

                                        부산에서....   쉬고  싶은   월천 올렸습니다.

* 질문&답 보다 여기 올리는 것이 많은 분들이 볼 것 같아서....  관리자님... 몇일 후... 옮겨 주셔도 관계없습니다...
   그러나 궁금해서....   빨리 원인을 알아야......   잠도 잘 것 같고 해서....
까막.윤창희 [ggamagui]   2010-04-05 09:04 X
버그는 아니고...
외국에서는 반올림 기법(?)이 우리랑 좀 개념이 다르다고 알고 있습니다.
무슨무슨법이라고 했었는데, 기억이... ㅠ.ㅠ

홀수인가 짝수인가로 올린다고...
실제 전체값에서는 우리 처럼 반올림하는 것보다 정확도가 높다고 하더군요.
이에 대해서 몇번 글이 올라왔었고, 그 답변도 나왔었던 것으로 기억합니다.
델마당에서였나... ^^



혼이 살아 있을까.... 대한민국.
박영목.월천 [gsbsoft]   2010-04-05 09:29 X
까막님 감사요... 그러나... 이것은 납득할 수 없습니다.  그래도 델마당 함 찾아보겠습니다 ^^
박영목.월천 [gsbsoft]   2010-04-05 09:37 X
http://www.delmadang.com/community/bbs_view.asp?bbsNo=3&bbsCat=43&indx=195044&keyword1=double&keyword2=반올림
..
..

Round(2.5); // 2를 돌려줌.
Rnd(2.5); // 3을 돌려줌.


한석희 님이 올리신 글-----------------------
> 가끔 round()를 사용해 실수를 정수로 바꾸려는 경우에
> 이상을 느끼는 분들의 글이 많이 올라옵니다...
>
> 반올림에는 무조건 4사5입하는 방법과 (보통 생각하는 방법)
> 유효숫자의 다음 자리가 5인 경우 마지막 유효숫자의 홀수/짝수를
> 구별해서 4사5입하는 두 가지 방법이 있습니다. (Banker's round)
>
> 델파이의 round()는 정수 변환을 위해 두번째 방법을 쓰는데,
> 이것은 반올림으로 인한 오차를 줄이기 위한 벙법입니다..
> 마지막 숫자가 5로 끝나는 숫자가 무작위로 입력될 경우,
> 그 앞의 숫자가 홀수 또는 짝수일 경우는 확률적으로 반씩일 것이므로,
> 무조건 4사5입하면 정답보다 큰 답이 나오고(올림에 해당하므로),
> 홀수/짝수를 구별하여 4사5입하면 정답에 보다 가까운 해를 구할수 있다
> 는 것입니다...

위의 것은 TEST해 보지 않음... 뭐 함수로 그렇게 만들었다면 납득할 수 있음...  그 나라에서 아니면... 수학의 통계에 의해...

그렇지만... 위의 경우와는 다르다고 생각됩니다...   아시는 분 더 많은 정보 부탁드립니다.

김도완 [purplecofe2]   2010-04-05 10:51 X
SetPrecisionMode(pmDouble)로 설정해서 사용해보셔요.
김태선 [cppbuilder]   2010-04-05 11:12 X
가끔씩 저런 문제에 부딛히고는 하는데
원래 실수 값을 double, float 에 저장할때는
실제는
3.175가 저장되는게 아니라,
3.174999999999999 가 저장됩니다.
이는 DEC 즉 십진수의 내부 저장 방법에 기인한 문제로
예전 8비트 시절부터 오랫동안 애용되어 오던 방법입니다.
다시 말하자면 컴퓨터가 10진수를 16진수로 기억되는데 따르는 문제입니다.
값에 따라서 기억되는 16진수의 값과 다시 10진수로 되돌렸을때의 값이 약간의 오차를 보이는
수치가 생기가 된 것이죠. 예의 3.175처럼.
그래서 이런 변환시에 생기는 오차를 줄이기 위해 여러가지 방법이 동원 되었습니다.

3.174999999999999 에 1000을 곱하면
3174.999999999999 가 되고
int 로 변환시는 .99999999999를 제거해버리니 저런 현상이 생깁니다.
이는 과거의 FPU(연산을 담당하는 CPU)에서도 그렇게 처리?한다고 기억이 드는군요. (자꾸 희미해지는 기억력 --;)

그래서 컴파일러를 위해서는 double, float의 계산은 double, float 형에서 모든 계산을 마치고,
int로는 최종 계산 값만 옮기면 문제가 해결됩니다.
이렇게 하면 컴파일러 최종 대입전에 스스로 오차를 바로 잡기 때문에 문제가 해결되나,
int로 바로 대입하면 최종 오차를 바로 잡는대신 int 값으로의 보정이 되기 때문에 .99999999999999999999가
잘려 버리게 됩니다.

언제인지는 정확히 기억이 나진 않지만 VC도 마찬가지 문제를 안고 있었는데
언제인가 부터 int로의 대입시에도 문제가 안되게 교정을 했다고 합니다.

이는 컴파일러 마다 다를 수 있는 사항이기 때문에
항상 double 값은 double 에서 모든 계산을 마치게 하고
int  result = result_double;
식으로 값만 옴기는 식으로 하면 오차를 없애게 됩니다.
박영목.월천 [gsbsoft]   2010-04-05 11:27 X


void __fastcall TForm1::Button5Click(TObject *Sender)
{
  SetPrecisionMode(pmDouble);     //3174, 3174  ==>  3174, 3175이것을 넣어니...
                                  //오 값이 바로 나왔네요....   김도완님 감사...

  float  fData   = 3.175;
  double dlData  = 3.175;

  int    valueF  = fData *1000;
  int    valueD  = dlData*1000;


  char str[100];

  wsprintf( str, "%d, %d", valueF,  valueD );

  ::MessageBox( this->Handle, str, "MSG", MB_OK );

  //결과: 3174, 3175
}
//---------------------------------------------------------------------------


void __fastcall TForm1::Button3Click(TObject *Sender)
{
   // Edit의 값 1.175를 1.15가 되게 하려고 한다.  0.05로 떨어지는 값이어야 함으로 0.05로 나누고
   // 소수점 아래를 버리고 곱하기 0.05 이렇게 하면 되잖아요
   // 그런데 Edit에 항상 1.175만 아니고 1.15(0.05로 떨어지는 값)도 사용자가 입력할 수 있지요

   //SetPrecisionMode(pmDouble);   //여기에 어떤 값을 넣어도 제대로 안나오네요

   double Value =  1.175;

   int n = Value / 0.05;

   Value = n * 0.05;

   ShowMessage( Value );         //1.15    정상  짝짝...


   Value =  1.15;

   n = Value / 0.05;

   Value = n * 0.05;

   ShowMessage( Value );        //1.1   <== 돌겠다....  Error   여기도 1.15가 되어야 하는 데...



   Value =  1.15;               // 그래서 중간값을 보기로 함...

   Value = Value / 0.05;

   ShowMessage( Value );        //여기까지 23   짝짝...

   n = (int)Value;

   ShowMessage( n );            //여기오면 22   엥... 경악!!!...

   Value = n * 0.05;            // 22 * 0.05

   ShowMessage( Value );       // 1.1



   Value =  1.15;               // 그러면 이렇게 해야 겠다.   double을 바로 계산

   Value = Value / 0.05;
   Value = Value * 0.05;

   ShowMessage( Value );       // 1.15   짝짝....



   Value =  1.175;               // 그러면 1.175도 이렇게 하여 1.15가  나온다면 웃기는 일이지만 나오면 좋겠다.

   Value = Value / 0.05;
   Value = Value * 0.05;

   ShowMessage( Value );       // 1.175 가 나온다...    이 정도라면 이것 좀 심각한 것 아닌가?  돌겠다...

   //---------------------------------------------------------------------------

   //이런 짓을 하도 하다 보니...  이제는 빌더의 마음을 알게 되더군요....  + 0.00001 을 더해줌


   Value =  1.15;

   Value = Value / 0.05;

   ShowMessage( Value );        //여기까지 23   짝짝...

   n = (int)(Value + 0.00001);      //그래서  이 부분에  0.1을 아니 혹 또 오차가 발생할까 싶어 0.00001을 더해 주었습니다. 어째도 int가 될 것이기 때문에...

   ShowMessage( n );            //여기도 23   짝짝...

   Value = n * 0.05;            // 23 * 0.05

   ShowMessage( Value );       // 1.15



   Value =  1.175;               // 이것도 잘 되네요...

   Value = Value / 0.05;         //23.5

   ShowMessage( Value );

   n = (int)(Value + 0.00001);  //그렇다면 뒤에 점이 없다면... 1을 빼버리나..????

   ShowMessage( n );            //23

   Value = n * 0.05;

   ShowMessage( Value );       //1.15   짝짝,.... -.-  그런데 이렇게 쓰야 하나...

}

//위의 논리를 적용시켜서... 아래를 이렇게 바꾸어 보았습니다.
//0.00001 이것보다 더 작아도 됩니다. 그러나 아주 작으면... 안됩니다.
//그러니까... double의 허용범위라고 해야 하나... 여하튼 실험해 보세요...
//SetPrecisionMode(pmDouble);  산술프로세서... 어떤 보정 하는 것 같은 데... 정확하게 어떻게 언제.. 해야할지..???
//뭔가 잘못되었다....

void __fastcall TForm1::Button6Click(TObject *Sender)
{
  float  fData   = 3.175;
  double dlData  = 3.175;

  int    valueF  = fData *1000 + 0.00001;
  int    valueD  = dlData*1000 + 0.00001;


  char str[100];

  wsprintf( str, "%d, %d", valueF,  valueD );

  ::MessageBox( this->Handle, str, "MSG", MB_OK );

  //결과: 3174, 3175
}
//---------------------------------------------------------------------------
김태선 [cppbuilder]   2010-04-05 11:31 X
오~ SetPrecisionMode(pmDouble) 라는게 있었군요.

오늘 좋은 것을 알았네요. ^^
Lyn [tohnokanna]   2010-04-05 11:32 X
실수는 걍 안믿는게..
박영목.월천 [gsbsoft]   2010-04-05 11:33 X
김태선님 말씀대로라면  VC도 언제가 부터 바뀌었으면... C++Builder 도 바꾸어 주어야 하지 않나?  생각합니다....
아~~~ 이것 때문에.... 어제 저녁부터 지금까지... 하고 있네요... -.-    
double에서 int로 갈 때마다... 신경을 곧두세우고... 해야하나...    어쟀던... 김태선님... 감사 ^^   
다른 정보 있는 분들 더 주십시오... 아직도 목마릅니다....   나에게 생수를.... 
박영목.월천 [gsbsoft]   2010-04-05 11:57 X
아~~~  위의 것과 별개로...   요즘... 사이버 대학 다닌다고  강의를 듣는 데... 교수님이 그러시더군요... 미래에는 디지털 + 아날로그 컴퓨터가 출현한다....  김태선님의 글을 2~3번 읽다보니... 저것이다. 컴퓨터가 실수로 모든 것을 처리하는 날... 그날이 디지털 + 아날로그 컴퓨터의 날이 아닌가?  퍼득 생각이 납니다. 손실없는...  실수가 앞으로의 컴퓨터의 미래인가?  또 무슨 잡념이... 위의 것이나 정리되혀 코딩할 생각은 않고....   ㅋㅋㅋ,  오... 우리의 린님... 감사요... 못보았습니다. 
김도완 [purplecofe2]   2010-04-05 13:44 X
델파이나 빌더는 기본적으로 Precision Mode가 pmExtended로 되어 있는데 이게 Message 수행 후나 DLL 로드 후에 바뀌는 경우가 많습니다. Extended(80비트)를 Double(64비트)로 낮추어서 VC++과 같은 결과를 얻게 할 수 있습니다. 혹자는 델파이나 빌더의 Math함수군에 이 설정이 영향을 준다고도 하는데 거기까지는 실험을 안해봐서 잘 모르겠네요.
김태선 [cppbuilder]   2010-04-05 14:52 X
제가 보기에는 ANSI 표준과의 호환성을 위해서인 것 같습니다.
과거의 프로그램들은 이러한 실수 연산의 특성을 이해하고 프로그래밍한 경우가 많고,
이러한 이슈는 이미 유명해서 볼랜드 개발자들이 몰라서 적용을 안한 것은 아닐 것입니다.

VC 경우는 ANSI 표준을 볼랜드만큼 지키지 못하고
개발을 선도한다는 자부심이 지나쳐서인지 몰라도 이런 부분은 과감히 고쳐버린 것 같습니다.

박영목.월천 [gsbsoft]   2010-04-05 17:12 X
김도완님, 김태선님 감사요..^^   하긴 저도 실수로 된 것 잘 다룰 일이 없어서... 요런 문제로 꼬이지 않았는 데...  휴~  그래도 다행입니다...  C++Builder가 정상이라고 하니....  제가 알아서 추가해서... 그렇게 보이도록 해야겠습니다. 정말 왠만하면... 실수로 깊게 들어가는 것은 주의해야 할 것 같습니다.  두 분 감사합니다 ^^
까막.윤창희 [ggamagui]   2010-04-05 17:48 X
좋은 내용이 많네요.

오차 보정에는 델파이 등등의 방식이 더 낫다는 것은 알지만...
습관적, 사회 통념적으로는 반올림이라...

제 경우는 * 1000 등으로 정수값을 만들어 처리하는 편입니다. ^^
박영목.월천 [gsbsoft]   2010-04-05 22:41 X
   double Value =  1.15;           // 그래서 중간값을 보기로 함...

   Value = Value / 0.05;

   ShowMessage( Value );      //여기까지 23   짝짝...

   int n = (int)Value;

   ShowMessage( n );            //여기오면 22   엥... 경악!!!...

   Value = n * 0.05;                 // 22 * 0.05

   ShowMessage( Value );       // 1.1

   //---------------------------------------------------

  올만에 목욕을 했습니다... 개운합니다.

  목욕을 하고 또 누가 답변을 주셨나... 하고 보았습니다.. 다시 첨부터 한번 더 읽었습니다.

  그런데 위에 제가 적은 것인데...  가만히 보니...

  첫번째 ShowMessage에서 23을 보여줍니다. 음밀히 말해서... 22.99999999999***   뭐뭐

  이렇게 보여 준는 것이 정상인 데...  ANSI의 기준을 따른다면...

  ShowMessage에서는 VC++처럼 보정을 하여 보여주는 군요....

  ShowMessage( FloatToStr(Value) ); 이것도 마찬가지군요...  보정하는 방법도 다 알고

  있으면서...  int로 넘어 갈때도 해주었다면 통일성도 있고 좋았을 것인 데...  아깝다....

  도대체 보정한 값을 얼마로 했을까?  요것 함수로 하나 만들어 두고 사용하면 괜찮을 것 같은 데...

  DoubleToInt();   혹 다음말에 있을까 찾아보았습니다. 없군요...

  그래서 고민하다가 23 - Value를 해보았습니다. 보정한 값을 알기 위해...  3.5527136788005E-15

  E-15....  헉... 지수표현은 알겠는 데... 정확하게... 찾아봐서 해야 할 것 .. 그래서 혹 보정값이

  있을까?  찾아보았습니다.  하나 찾았습니다. 이 분도 개인 같은 데... 보정값이... 맞는지는 모르겠습니다.


부동 소숫점 실수 사용시 유의 사항  C/C++ 
2007/01/17 13:47

http://blog.naver.com/elky84/10013104493



부동 소수점 실수란, float이나 double형을 말하는데 C언어 등 주요 언어에서 사용되는 소수점의 형태이다. 부동 소수점은 표현하는 수의 범위를 크게 만들기 위해 정밀함을 포기 했는데, 때문에 릴리즈 빌드에서 부동 소수점 실수는 값이 0.000001f(EPSILON)만큼 소실된다. 이것은 소수점끼리의 연산에서는 큰 지장이 없을 때가 많지만, 정수와 곱해줄 때 1.0f에서, 0.000001f 만큼 소실되어 버리면, 0.999999f 가 되어, 0과 곱하는 것이 되어버려 큰 계산 착오를 일으키게 됩니다.

이 문제를 알고 있다고 해도, 소수점을 사용하는 코드 어느 한곳에서만 빼먹어도 곤란한 상황이 나올 수가 있기 때문에, 정밀한 소수점 연산 클래스를 만들어, 부동 소수점 실수를 아예 사용하지 않는다고도 하는데, 내장 타입 (Built In Type)만큼 빠른 건 없기 때문에, 부동 소수점을 사용하되 부동 소수점 실수 class를 만들어두고, 필요할 때 마다 EPSILON만큼 더 해주어 이 문제를 해결하기도 하는 등 소실되는 값을 보정해주는 방법이 더 선호되는 편입니다.



const float EPSILON = 0.000001f;
class CSafeFloat

{

private:

        float m_fData;

public:

        float GetData()

        {

               return m_fData;

        }

        void operator=(const float &pfData)

        {

               m_fData = pfData + EPSILON;

        }
        bool operator==(const float &pfData)

        {

               If(abs(m_fData, pfData) <= EPSILON){
                       return true;
               }
               return false;

        }

        CSafeFloat(float fData)

        {

               m_fData = fData;

        }



        float operator+()

        {

               return m_fData + EPSILON;

        }



        ~CSafeFloat()

        {

        }

};



1. float(단정도) 형식과 double(배정도) 형식의 변수끼리는 비교하면 안된다. 같은 수를 가지더라도 틀릴 수 있다. 부동소수점 방식의 변수일 경우 허용 오차를 생각한 후 비교한다. (EPSILON 정도의 값)

2. 2진수를 사용하는 컴퓨터에서는 0.1 과 같이 2진수로 변환했을 때 무한소수로 바뀌는 수의 경우 정확히 표현하지 못한다.

3. 연산 결과가 마지막 소수 자리까지 정확하지 않을 수 있다. 유효 자리 수를 벗어나면 그 이후로는 정확한 연산이 되지 않는다.

4. 오차를 줄이기 위해 정수 연산을 먼저 한 후 부동 소수점 연산을 한다.

5. 최대한 비슷한 크기의 부동 소수점 연산을 한다.

[출처] 부동 소숫점 실수 사용시 유의 사항|작성자 엘키
이경문 [gilgil]   2010-04-05 22:46 X
요즘 컴파일러에서는 IEEE 754에서 제정한 실수(single precision binary floating-point)를 사용합니다.
화면에 표현을 하는 과정에서 10진수와 관계가 있지 내부적으로는 전부 2진수로 처리를 합니다.
박영목.월천 [gsbsoft]   2010-04-05 23:00 X
이경문님 감사요.. ^^  역시 짱입니다...   제가 배운 것이 없어서... 무슨 말인지는 잘 모르겠지만....   또 누가 보정해 주시길... 바랍니다...   저는 위의 것을 보고 int로 변환시 그냥  실수 + 0.000001  더해서 보정할 생각이었습니다. 대부분은 다 될 것 같아서.... 혹... 정말... 아슬아슬하게... 넘어갈 경우.. 있을까?  해보아야 소수점 2~3자리까지만 화면상에 표기할 것이기 때문에...   뭔가 나올 것 같은 데....  또 다른 분... 댓글을 부탁합니다.
Lyn [tohnokanna]   2010-04-05 23:07 X
월천 // 입실론의 크기가 변화되어야 한다는 거죠..

예를들어 Double 형에서  128798754986789.455645645689 라는 숫자의 입실론으론 0.0001 이면 충분하겠지만 0.123534683754834758 라는 숫자의 입실론으론 0.0001은 충분 하지 못하다는거죠.

결과값을 대충 예상 해야 된다는거겠네요... 0.00001을 더하는건 보정하는게 아니라 오차를 증가시키는겁니다.
박영목.월천 [gsbsoft]   2010-04-05 23:10 X
위의 글과 이경문님의 글을 5번 정도 읽고 대략 이해를 했습니다.  실수 + 0.000001  뒤에 보정값을 앞의 실수값의 지수의 값에 따라 좌우되니... 뒤에 보정값도 다르게 바뀌어야 한다. 이렇게 해석되는 군요... 맞는지.. 그런지...  역시... 많이 배워야 합니다....   이것 뭐 알아갈수록... 더 힘들어지는 군요... ㅋㅋㅋ..... 여기에 맞는 성경구절이 생각납니다.  "지혜가 많으면 번뇌도 많으니 지식을 더하는 자는 근심을 더하느니라" - 성경 전도서
이경문 [gilgil]   2010-04-05 23:11 X
나중에 실수 처리를 하는 것에 대해서 좀 더 간략하게 설명을 해 보도록 하겠습니다. ^^
박영목.월천 [gsbsoft]   2010-04-05 23:12 X
에고 답변을 적고 있을 때  Lyn이 답변을 주셨네요.... ㅋㅋㅋ  감사합니다. 제가 이해한 것이 맞군요... 5번 읽고 이해를 하다니...    다른 분 또....  계속...  머리를 사용해 주시기 바랍니다....
박영목.월천 [gsbsoft]   2010-04-05 23:14 X
이경문님 감사요... 꼭 부탁드립니다....  ^^   그러나...  더 의견을 추가하실 분은 계속...  댓글 부탁드립니다.
Lyn [tohnokanna]   2010-04-05 23:24 X
예를들어 3.14를 표기할때 3.139999999999999999999가 될 수도 있지만 3.140000000000000000001이 될 수도 있다는..

입실론이 0.00000001 인 경우라면

|3.14 - 3.139999999999999999999| < 0.00000001이고
|3.14 - 3.140000000000000000001| < 0.00000001 이기 때문에 세 숫자는 전부 같다고 봅니다 실수에서는..
박영목.월천 [gsbsoft]   2010-04-05 23:35 X
똑똑한  태선님, 경문님, 린님....   저같이 평범한 프로그래머가 일반적으로 그냥 편하게 사용할 수 있는 DoubleToInt, FloatToInt 같은 함수 하나 부탁드립니다.   사실 결론은 이것인 데... 생각보다... 생각할 것이 많네요...  만들었다고 해도... 이게 정말 정확한지...  두렵습니다....   꼭 필요할 것 같습니다. 한번씩이지만....   그래도 여러분들이 답변을 많이 주셔서... 너무나 감사히 생각합니다. 그래서 나는 행복합니다 ^^
이경문 [gilgil]   2010-04-05 23:43 X
잘못된 EPSILON 사용예입니다. 참고하시기 바랍니다.
http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=979
이경문 [gilgil]   2010-04-05 23:53 X
박영목 / 그게 정말 어려운 일입니다. 1/3 값은 아무리 bit수가 높은 실수형을 쓴다 하더라도 정확한 값을 표현하기는 어렵습니다. 금융권에서는 BigInteger라든지 BigFloat와 같은 클래스를 회사 자체에서 별도로 만들어 사용을 하고 있습니다.
박영목.월천 [gsbsoft]   2010-04-06 00:06 X
이경문님 감사요...^^  충분히 이해했습니다...  사용예를 보지 않고 아까 경문님이 댓글 다신 2번째 글을 보고 naver에서 가져온 class  보고... 또 보고하여...  그리고 올려 주신 예제도 잘 보았습니다.  현재까지 제가 적용시킬 것은 사용자가 입력이 소수점 3짜리를 넘지 않기 때문에...  + 000001 만해도 보정이 충분할 것이라 생각합니다. 그리고 그 다음 계산 부터는 정수화, 아까 까막님 처럼 1000을 곱해서 ms 단위로 처리를 할 것이기 때문에...  그렇게 염려하지 않습니다.  이유도 알았고...   앞으로 바램은 누가...  논리적으로 날카롭고 똑똑한 분들이...  보정 함수를 하나 만들어 주시는 것입니다. 제가 만들려고 해도... 이론적인 머리가 딸립니다...  만들어 내는 사람과 보고 아... 맞다는 하는 사람이 있듯이 저는 보고 아~   이것다... 정도 구별할 정도의 수준 밖에 되지를 못합니다. 이런 것은 또 혼자 하면 마음에 부담도 클 것이고... 이곳에 똑똑한 분들이 고민하여 서로 의논해서...  하나 만들어 주시면... 감사하겠습니다.  물론 시일이 걸리고... 보정 추가 해야겠지요...  물론 이런 경우 만들고 나면 몇 줄 되지는 않겠지만,... 생각보다 난이도가 있을 것 같습니다.  답변을 주신 모든 분들께 감사를 드립니다 ^^
박영목.월천 [gsbsoft]   2010-04-06 00:10 X
글을 쓰고 있을 때 또 답변을 주셨네요... ㅋㅋㅋ   아 그렇구나....  어렵다고 생각은 했지만... 경문님 말씀대로 상당히... 난이도가 있는 함수 같군요....   실수와 컴퓨터의 정수 사이에 이런 큰 벽이 있을 줄이야....  많은 것을 깨달은 하루였습니다. 감사합니다. ^^
이경문 [gilgil]   2010-04-06 00:26 X
실수가 bit로 어떻게 표현되는지를 이해하시면 각각의 어플리케이션의 경우마다 충분히 대처하실 수 있을 것입니다.
http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tip&no=980

정확히 말씀을 드리자면 "10진수에 익숙한 입장에서 소수점 몇자리까지만 정확하면 된다"라는 생각에 의해 프로그래밍을 하였을 경우 예상치 않은 결과를 나을 수도 있습니다. 이는 실수를 요즘에는 2진수로 처리를 하기 때문에 그렇습니다.

예를 들면 1.1은 십진수로 표현을 하면 소수점 한자리에서 딱 끝이 나지만 float로 표현을 하면 significand가 0x8ccccccccccc.... 가 되어 버립니다. 정확히 표현을 할 수가 없다는 거죠. 그래서 결국 0x8ccccd의 값으로 반올림을 해 버리는 거구요.

시간이 걸리더라도 실수를 컴퓨터에서 어떻게 표시를 하는지, 그리고 예전에는 10진수 기반으로 표시를 했다가 왜 2진수 기반으로 넘어 갔는지를 곰곰히 생각해 보시면 해답을 스스로 찾으실 수 있을 것입니다.
박영목.월천 [gsbsoft]   2010-04-06 00:37 X
넵....  알겠습니다....  참 어렵군요....   오류가 일어날 가능성이 있다...  현재까지는 시각을 입력하면... 0.05의 배수가 아니다 박스 띄우고 나가게 해두었습니다... 그런데 사용자 입장에서 이게 입력하는 데... 제법 귀찮다는 생각이 들었습니다. 그래서... 보정을 해서... 제 프로그램에서... 원하는 가까운 값으로 자동으로 바꾸어 주는 부분을 넣을 생각이었는 데...   그대로 두어야 할 것 같군요....  넣어도 별 이상은 없을 것 같은 데... -.-    이번에는 다관절이라... 시각 입력이 몇개 더 추가되어서... 입력하는 사람이 짜쯩을 낼 것 같아... 이런 계산을 해보았는 데... 갈수록 태산이군요....   여하튼... 위의 글들을 참고해서... 해결하도록 하겠습니다...   섬세하게... 신경써주셔서.. 너무나 감사합니다... ^^  
이경문 [gilgil]   2010-04-06 01:00 X
쉽게 말씀을 드리자면
1/3은 10진수로 나타내면 0.3333333.... 끝이 없죠.
하지만 만약 사람의 손가락이 왼손, 오른손 합쳐서 10개가 아니고 3개라면,
그래서 전 세계적으로 3진수가 사용이 되었다면
1/3(10진수) 은 0.1(3진수)로 표시되었을 것입니다.

컴퓨터는 bit가 연산 단위의 가장 기본단위이기 때문에
사람 손가락 수에 상관 없이 컴퓨터는 2진수 기반으로 발전할 수 밖에 없습니다.
그러기 때문에 반드시 2진수의 이해가 필요합니다.

결론을 지어서 10진수로는 쉽게 표현이 가능한 소수가
2진수로 표현하기 힘든 경우가 생길 수도 있다는 정도를 염두에 두시면 될 것 같습니다.
박영목.월천 [gsbsoft]   2010-04-06 01:10 X
ㅋㅋㅋ  예...  알겠습니다...   혹시나 하고 리플레쉬 했더니...  또 답변을 주셨네요...^^   잘 알겠습니다...  지금 나름대로 적용시켜 보고 있습니다.  어제 밤과 같은 난감, 불안한 상황은 아닙니다.  알기 때문에...  마음이 좀 편합니다. 어제는 정말 사람 미치겠더군요...  VC++ 되는 데... 안되니.... ㅋㅋㅋ    감사합니다 ^^
박영목.월천 [gsbsoft]   2010-04-06 02:05 X
역시 이경문님 똑똑하십니다. 제가 나대로 실수 + 0.000001 이렇게 보정을 하여 실험을 하니
문제가 발생하는 군요... -.-   그러다가... 갑자기 생각났습니다. 보정 방법....


저의 7번째 댓글 내용이 머리를 스치고 지나 갔습니다.


  ".....  ANSI의 기준을 따른다면...   ShowMessage에서는 VC++처럼 보정을 하여 보여주는 군요....

  ShowMessage( FloatToStr(Value) ); 이것도 마찬가지군요...  보정하는 방법도 다 알고

  있으면서...  int로 넘어 갈때도 해주었다면 통일성도 있고 좋았을 것인 데...  아깝다....   "


  어악~~~~~    답이 여기에 있네요... ㅋㅋㅋㅋㅋ  천재들이 만든 C++Builder의 함수를 그대로 사용
  하면 뭐 효율은 그렇게 빠르지 않겠지만... 나름대로 훌륭한 실수 보증 함수를 만들 수 있겠구나!!!!
  어느 정도 검정도 되었고... ㅋㅋㅋ   그래서 만들었습니다.  풀그림 오래 하다보니 하였튼 잔꾀만 많이
  늘어서... ㅋㅋㅋ   이론, 논리 이런 것 더 발달해야 하는 데... 늘 이렇게 잔꾀만 부리는... 한심한 월천



   double Value =  1.15;

   int n = Value / 0.05;

   Value = n * 0.05;

   ShowMessage( Value );        //1.1    Error...   1.15가 나와야 하는 데....
   //---------------------------------------------------


   Value =  1.15;

   Value /= 0.05;

   Value = StrToFloat( FloatToStr( Value ) );    // 이 과정을 거치면 실수가 보정되어...

   n = Value;                                    // 22.9999 아니라  23.0 이 들어가게 됩니다.

   Value = n * 0.05;

   ShowMessage( Value );    //1.1 이 아닌  1.15 정확히 나오는 군요. 정상 짝짝,  1.175를 대입해도

   에고~~~~~   지금 나대로 보정해서 실험하니 안돼서... 눈만 껌벅이다가... 이 간단한 것을....

   알고 적용하니 간단히 해결되었습니다. ^^                      O Happy Day~~~  

   실수 보정하실 분들...  위의 것을 사용하십시오...    감사합니다 ^^

   다시 한번 댓글에 동참해 주신 모든 분들께 감사를 드립니다. 이렇게... 계속... 댓글을 달면서...

   생각하지 않았다면... 발견하지 못했을...  것을.....  아~~ 흑~~~  울고 싶다...  정말 감사합니다 ^^
박영목.월천 [gsbsoft]   2010-04-06 02:19 X
   double Value =  1.15;

   Value /= 0.05;

   int n = StrToFloat( FloatToStr( Value ) );

   간단히 요런식으로 해도 되겠네요...  float도 물론 가능하겠지요.... ^^
미노 [wyb330]   2010-04-06 11:51 X
델파이에서는 반올림 에러를 최소화하기 위해 IEEE 754 형식의 부동소수점 말고 고정 소수점 형식의 currency 실수
타입을 제공하고 있습니다. 돈 계산에서 이런 오차가 발생하면 큰 일 날테니까요. C++ 빌더에서 이런 타입을
지원하는지는 모르겠네요.
남병철.레조 [lezo]   2010-04-06 20:09 X
댓글수가;; 이 기회에 실수에 대해 댓글로 뽕을 뽑으시려나들봅니다.. --;

+ -

관련 글 리스트
18018 C++Builder 6.0에서 double 좀 이상합니다. 박영목.월천 5321 2010/04/05
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.