출처 : http://support.microsoft.com/kb/125056/ko


기술 자료: 125056 - 마지막 검토: 2004년 1월 19일 월요일 - 수정: 2.0

INFO: 부동 소수점 연산의 정밀도와 정확도

이 문서는 이전에 다음 ID로 출판되었음: KR125056

이 페이지에서

모두 확대 | 모두 축소

요약
부동 소수점수의 연산은 정밀도, 반올림 및 정확도에 따라 예상 밖의 결과를 얻는 경우가 많습니다. 다음은 부동 소수점 연산에서 지켜야 할 4가지...

부동 소수점수의 연산은 정밀도, 반올림 및 정확도에 따라 예상 밖의 결과를 얻는 경우가 많습니다. 다음은 부동 소수점 연산에서 지켜야 할 4가지 일반 규칙입니다.
  1. 단정밀도 수와 배정밀도 수의 연산결과는 대부분 단정밀도 수 연산 결과와 같은 정확하지 않은 결과를 리턴합니다. 배정밀도 수가 필요하면 상수를 포함한 해당 연산의 모든 숫자를 배정밀도 수로 지정해야 합니다.
  2. 컴퓨터에서는 아무리 단순한 부동 소수점 숫자 값이라도 정확하게 표현되지 않습니다. 대부분의 부동 소수점 값은 이진수 값으로 정확하게 표현할 수 없습니다. 예를 들어, 0.1을 이진수로 표현하면 무한히 반복되는 0.0001100110011...이므로, PC를 포함한 이진법을 사용하는 모든 컴퓨터에서는 완전히 정확하게 표현할 수 없습니다.
  3. 연산 결과가 마지막 소수 자릿수까지 정확하지 않을 수 있습니다. 사람이 계산하는 "정확한" 값과 이진법을 사용하는 한정된 정밀도로 부동 소수점 연산을 수행하는 컴퓨터에서의 결과 값 사이에는 항상 약간의 차이가 있습니다.
  4. 서로 같은지 확인하기 위해 두 개의 부동 소수점 값을 비교하지는 마십시오. 규칙 3에 따라 "반드시" 같아야 하는 두 숫자 사이에도 항상 약간의 차이가 납니다. 그 대신 항상 숫자들이 거의 근사한 값인지 확인하십시오. 즉, 두 수의 차이가 아주 작은지 또는 무의미한지 확인하십시오.

추가 정보
일반적으로 앞에서 설명한 규칙은 C, C++, 어셈블러를 포함하여 모든 언어에 적용됩니다. 아래의 예제에서는 FORTRAN PowerStatio...

일반적으로 앞에서 설명한 규칙은 C, C++, 어셈블러를 포함하여 모든 언어에 적용됩니다. 아래의 예제에서는 FORTRAN PowerStation 사용과 관련된 몇 가지 규칙을 보여줍니다. C로 작성된 마지막 예제을 제외하고 모든 예제가 옵션 없이 FORTRAN PowerStation 32를 사용하여 컴파일되었습니다.

숫자 상수에 대한 자세한 내용은 Microsoft FORTRAN과 함께 제공되는 FORTRAN 설명서를 참조하고, 부동 소수점 값의 내부 표현에 대한 자세한 내용은 36068  (http://support.microsoft.com/kb/36068/EN-US/ ) 문서를 참조하십시오.

예제 1

첫 번째 예제는 다음 두 가지 사항을 보여줍니다.

  • FORTRAN 상수는 기본적으로 단정밀도 수이지만, C 상수는 기본적으로 배정밀도입니다.
  • 단정밀도 수를 포함하는 연산은 모든 연산되는 수가 단정밀도 수인 연산보다 정확하지는 않습니다.
단정밀도 상수 1.1로 초기화된 후 y는 단정밀도 변수만큼 정확하지 않습니다.
   x = 1.100000000000000  y = 1.100000023841858
단정밀도 값을 배정밀도 값으로 곱한 결과는 두 개의 단정밀도 값을 곱한 것만큼이나 정확하지 않습니다. 두 가지 연산 모두 두 개의 배정밀도 값을 곱했을 때보다 오류가 많습니다.
   true = 1.320000000000000 (두 개의 배정밀도 값을 곱한 경우)
   y    = 1.320000052452087 (배정밀도 값과 단정밀도 값을 곱한 경우)
   z =1.320000081062318 (두 개의 단정밀도 값을 곱한 경우)

예제 코드

C Compile options: none

       real*8 x,y,z
       x = 1.1D0
       y = 1.1
       print *, 'x =',x, 'y =', y
       y = 1.2 * x
       z = 1.2 * 1.1
       print *, x, y, z
       end

예제 2

예제 2에서는 2차 방정식이 사용됩니다. 이 예제에서는 배정밀도 연산도 완벽하지 않으며 작은 오류가 결과에 큰 변화를 줄 수 있는 경우 그 전에 연산 결과를 테스트해야 함을 보여줍니다. 예제 2에 나와 있는 제곱근 함수의 입력은 아주 약간 틀리지만 역시 올바르지 않은 값입니다. 배정밀도 연산에 아주 작은 오류도 없다면 결과는 다음과 같아야 합니다.
   Root =   -1.1500000000
그러나, 이진법을 사용하는 컴퓨터에서는 다음과 같은 오류가 생성됩니다.
run-time error M6201: MATH
- sqrt: DOMAIN error

예제 코드

C Compile options: none

       real*8 a,b,c,x,y
       a=1.0D0
       b=2.3D0
       c=1.322D0
       x = b**2
       y = 4*a*c
       print *,x,y,x-y
       print "(' Root =',F16.10)",(-b+dsqrt(x-y))/(2*a)
       end

예제 3

예제 3에서는 최적화를 켜지 않은 경우에도 최적화가 발생하여 값이 일시적으로 예상한 것보다 높은 정밀도를 유지할 수 있다는 점과 두 개의 부동 소수점 값이 같은지 비교하는 것은 현명하지 못함을 보여줍니다.

이 예에서는 두 개의 값이 모두 같거나 같지 않습니다. 첫째 IF에서는 값 Z가 아직 보조 프로세서의 스택에 있어 Y와 같은 정밀도를 갖습니다. 따라서 X가 Y와 같지 않아 첫째 메시지가 인쇄됩니다. 둘째 IF가 처리될 때는 Z가 메모리에서 로드되어야 하므로 X와 같은 정밀도와 값을 갖게 되어 둘째 메시지도 인쇄됩니다.

예제 코드

C Compile options: none

       real*8 y
       y=27.1024D0
       x=27.1024
       z=y
       if (x.ne.z) then
         print *,'X does not equal Z'
       end if
       if (x.eq.z) then
         print *,'X equals Z'
       end if
       end

예제 4

예제 코드 4의 첫째 부분에서는 1.0에 인접한 두 숫자 사이에서 가능한 가장 작은 차이를 계산합니다. 1.0의 이진 표현에 단일 비트를 추가하는 방법을 통해 계산합니다.
   x   = 1.00000000000000000  (1.0보다 1비트 큼)
   y   = 1.00000000000000000  (1.0)
   x-y =  .00000000000000022  (가능한 가장 작은 차이)
일부 FORTRAN 버전에서는 숫자를 표시할 때 반올림하므로 본질적인 숫자의 부정확함을 쉽게 알아차릴 수 없습니다. 이 때문에 x와 y를 표시하면 같은 값으로 표시됩니다.

예제 코드 4의 둘째 부분에서는 10.0에 인접한 두 숫자 사이에서 가능한 가장 작은 차이를 계산합니다. 10.0의 이진 표현에 단일 비트를 추가하는 방법을 통해 계산합니다. 10에 인접한 숫자들 간의 차이는 1에 인접한 숫자들 간의 차이보다 큽니다. 즉, 숫자의 절대값이 클수록 주어진 비트 숫자에서 정확하게 저장되지 않을 가능성이 늘어납니다.
   x   = 10.00000000000000000  (10.0보다 1비트 큼)
   y   = 10.00000000000000000  (10.0)
   x-y =   .00000000000000178
해당 숫자가 1비트만 차이남을 보여주기 위해 숫자의 이진 표현도 표시했습니다.
   x = 4024000000000001 Hex
   y = 4024000000000000 Hex
예제 코드 4의 마지막 부분에서는 간단한 비순환 10진 값은 대개 순환 소수로 이진 표현할 수 있음을 보여줍니다. 이 경우 x=1.05는 가수(mantissa)에서 반복 인자 CCCCCCCC....(16진수)를 필요로 합니다. FORTRAN에서는 가능한 가장 높은 정확도를 유지하기 위해 마지막 자릿수 "C"를 "D"로 반올림합니다.
   x = 3FF0CCCCCCCCCCCD (1.05D0의 16진 표현)
반올림해도 결과가 완벽하게 정확하지는 않습니다. 첫째 자릿수를 제거한 후에 볼 수 있는 최소 유효 자릿수 뒤에 몇 가지 오류가 있습니다.
   x-1 = .05000000000000004

예제 코드

C Compile options: none

       IMPLICIT real*8 (A-Z)
       integer*4 i(2)
       real*8 x,y
       equivalence (i(1),x)

       x=1.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *

       x=10.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *
       print "(1x,'x  =',Z16,' Hex  y=',Z16,' Hex')", x,y
       print *

       x=1.05D0
       print "(1x,'x  =',F20.17)", x
       print "(1x,'x  =',Z16,' Hex')", x
       x=x-1
       print "(1x,'x-1=',F20.17)", x
       print *

       end

예제 5

C에서 부동 소수점 상수는 기본적으로 배정밀도 수입니다. float 값을 나타내려면 "f"를 사용하십시오(예: "89.95f").
   /* Compile options needed: none
   */ 

   #include <stdio.h>

   void main()
   {
      float floatvar;
      double doublevar;

   /* Print double constant. */ 
      printf("89.95 = %f\n", 89.95);      // 89.95 = 89.950000

   /* Printf float constant */ 
      printf("89.95 = %f\n", 89.95F);     // 89.95 = 89.949997

   /*** Use double constant. ***/ 
      floatvar = 89.95;
      doublevar = 89.95;

      printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.950000

   /*** Use float constant. ***/ 
      floatvar = 89.95f;
      doublevar = 89.95f;

      printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.949997
   }

본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft FORTRAN PowerStation 1.0 Standard Edition
  • Microsoft Fortran PowerStation 1.0a for MS-DOS
  • Microsoft FORTRAN PowerStation 32
  • Microsoft Visual C++ 1.0 Professional Edition
  • Microsoft Visual C++ 1.5 Professional Edition
  • Microsoft Visual C++ 1.51
  • Microsoft Visual C++ 2.0 Professional Edition
  • Microsoft Visual C++ 4.0 Standard Edition
  • Microsoft Visual C++ 5.0 Enterprise Edition
  • Microsoft Visual C++ 6.0 Enterprise Edition
  • Microsoft Visual C++ 5.0 Professional Edition
  • Microsoft Visual C++ 6.0 Professional Edition
  • Microsoft Visual C++, 32비트 Learning Edition 6.0
키워드: 
kbinfo kblangfortran kblangc kbcode kbfortranps KB125056
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스

댓글을 달아 주세요