procedure TForm1.Button1Click(Sender: TObject);
var
  a: Extended;
  s: String;

begin
  a:=어떻게어떻게 뽑아낸 실수값;
  s:=FloatToStr(Trunc(a));
  Memo1.Lines.Add(s);
end;

 

 

위와 같은 코드를 돌렸는데 손으로 계산해보면 604 가 나올 값이 603 이 나오는 현상이 있었음.

 

 

실수타입의 저장형태와 연산과정에서 2진수로 처리된 것이 십진수로 변환되면서 일어나는 문제이다.

만약 0.1 이 2진수로 저장될 경우, 아래와 같은 형태로 저장된다.

 

0.1 x 2 -> 0.2    0 

0.2 x 2 -> 0.4    0 

0.4 x 2 -> 0.8    0 

0.8 x 2 -> 1.6    1 

0.6 x 2 -> 1.2    1 

0.2 x 2 -> 0.4    0  (여기와 두번째 행은 같음. 즉 반복시작) 

0.2 x 2 -> 0.4    0 

0.4 x 2 -> 0.8    0 

0.8 x 2 -> 1.6    1 

0.6 x 2 -> 1.2    1 

0.2 x 2 -> 0.4    0  (여기서 또 반복시작) 

        :

 

0.1은 0.0001100110011001100110011.... 와 같은 영원한 순환소수임.

따라서 시스템 내부의 유한비트안에 저장할 자리수만 남기고 나머지 무한부분은 절사.

그러므로 시스템 내부적으로 저장된 2진수를 다시 10진수 형태의 실수로 전환시 오차 발생.

 

위에서 알 수 있는 실수연산의 문제점으로 인해 Trunc(604) 가 603으로 나와버리는 현상이 일어나는 경우가 있음.

그러므로 이것을 방지하기 위해 일반적으로 권장되는 방법은 계산결과값에 영향을 미치지 못하면서,

실수연산에서 일어날 수 있는 오차를 줄이기 위한 범위 내의 허용가능수치를 더해주는 방법이다.

 

원래 있던 코드를 아래와 같이 변경하면 정확하게 604라는 값을 출력한다.

굵은글씨와 주황색(?) 바탕으로 처리되어 있는 라인에 나름의 허용가능수치인 0.0000001 을 더해줬다.

 

 

 

 

 

procedure TForm1.Button1Click(Sender: TObject);
var
  a, b: Extended;
  s: String;

begin
  a:=어떻게어떻게 뽑아낸 실수값;

  b:=Trunc(a+0.0000001);
  s:=FloatToStr(Trunc(b));
  Memo1.Lines.Add(s);
end;

 

 

 

 

 

 

 

그냥 Trunc 함수 자체를 오버로딩해버리는 방법도 생각해볼 수 있다.

function Trunc(X: Real): Int64;
begin
  Result:=System.Trunc(X+0.0000001);
end;






Posted by bloodguy
,