문제

아래 코드에서 나는 사용한다 mpf_add 두 개의 플로팅 값의 문자열 표현을 추가합니다. 이 시점에서 내가 이해하지 못하는 것은 이유입니다 2.2 + 3.2 = 5.39999999999999999999999999999999999999. 나는 그렇게 생각했을 것이다 gmp 줄만큼 똑똑했습니다 5.4.

GMP가 어떻게 떠오르는 지 이해하지 못하는 것은 무엇입니까?

(BTW, 내가 이것을 처음 썼을 때, 나는 소수점을 삽입하는 방법을 확신하지 못했기 때문에 끝에 플러스/마이너스 숫자가 있습니다)

BSTR __stdcall FBIGSUM(BSTR p1, BSTR p2 ) {
  USES_CONVERSION;

  F(n1);
  F(n2);
  F(res);

  LPSTR sNum1 = W2A( p1 );
  LPSTR sNum2 = W2A( p2 );

  mpf_set_str( n1, sNum1, 10 );
  mpf_set_str( n2, sNum2, 10 );

  mpf_add( res, n1, n2 );

  char * buff =  (char *) _alloca( 1024 );
  char expBuffer[ 20 ];
  mp_exp_t exp;

  mpf_get_str(buff, &exp, 10, 0, res);

  char * temp = ltoa( (long) exp, expBuffer, 10 );
  if (exp >= 0) {
    strcat(buff, "+" );
  }
  strcat(buff, expBuffer );

  BSTR bResult = _com_util::ConvertStringToBSTR( buff );
  return bResult;
}
도움이 되었습니까?

해결책

이것은 이진 환경에서 부동 소수점 산술을 사용하는 고유 한 오류 때문입니다.

참조 IEEE 754 자세한 내용은 표준입니다.

다른 팁

야생 조수 사육 특권 말했다.

플로팅 포인트 번호 대신 바이너리 코드 소수점을 사용하는 경우 더 나은 결과를 얻을 수 있지만 실제로 라이브러리로 안내 할 수는 없습니다.

나는 결국 이것을 직접 대답하게되었다. 저를위한 해결책은 학교에서했던 일을 코드로하는 것이 었습니다. 이 방법은 다음과 같이 작동합니다.

  1. 각 숫자를 가져 와서 소수점 오른쪽의 숫자 수가 동일해야합니다. 따라서 추가하는 경우 2.1 그리고 3.457, 첫 번째를 '정상화'하십시오 2.100. 소수점의 오른쪽에있는 숫자 수를 기록하십시오.이 경우 3 개.
  2. 이제 소수점을 제거하고 사용하십시오 mpz_add 이제 두 숫자를 추가하기 위해 2100 그리고 3457. 결과는입니다 5557.
  3. 마지막으로, 오른쪽에서 3 자 (이 경우)의 3 자 (이 경우)를 다시 삽입하여 정답을 제공합니다. 5.557.

나는 vbscript (아래)의 솔루션을 프로토 타입했습니다.

function fadd( n1, n2 )
    dim s1, s2, max, mul, res
    normalise3 n1, n2, s1, s2, max
    s1 = replace( s1, ".", "" )
    s2 = replace( s2, ".", "" )
    mul = clng(s1) + clng(s2)
    res = left( mul, len(mul) - max ) & "." & mid( mul, len( mul ) - max + 1 )
    fadd = res
end function

sub normalise3( byval n1, byval n2, byref s1, byref s2, byref numOfDigits )
    dim a1, a2
    dim max
    if instr( n1, "." ) = 0 then n1 = n1 & "."
    if instr( n2, "." ) = 0 then n2 = n2 & "."
    a1 = split( n1, "." )
    a2 = split( n2, "." )
    max = len( a1(1) )
    if len( a2(1) ) > max then max = len( a2( 1 ) )
    s1 = a1(0) & "." & a1(1) & string( max - len( a1( 1 )), "0" )
    s2 = a2(0) & "." & a2(1) & string( max - len( a2( 1 )), "0" )
    numOfDigits = max
end sub

마지막으로 시각적 C ++ (아래).

#define Z(x) mpz_t x; mpz_init( x );

BSTR __stdcall FADD( BSTR p1, BSTR p2 ) {
  USES_CONVERSION;

  LPSTR sP1 = W2A( p1 );
  LPSTR sP2 = W2A( p2 );

  char LeftOf1[ 1024 ];
  char RightOf1[ 1024 ];
  char LeftOf2[ 1024 ];
  char RightOf2[ 1024 ];
  char * dotPos;
  long numOfDigits;
  int i;
  int amtOfZeroes;

  dotPos = strstr( sP1, "." );
  if ( dotPos == NULL ) {
    strcpy( LeftOf1, sP1 );
    *RightOf1 = '\0';
  } else {
    *dotPos = '\0';
    strcpy( LeftOf1, sP1 );
    strcpy( RightOf1, (dotPos + 1) );
  }

  dotPos = strstr( sP2, "." );
  if ( dotPos == NULL ) {
    strcpy( LeftOf2, sP2 );
    *RightOf2 = '\0';
  } else {
    *dotPos = '\0';
    strcpy( LeftOf2, sP2 );
    strcpy( RightOf2, (dotPos + 1) );
  }

  numOfDigits = strlen( RightOf1 ) > strlen( RightOf2 ) ? strlen( RightOf1 ) : strlen( RightOf2 );

  strcpy( sP1, LeftOf1 );
  strcat( sP1, RightOf1 );
  amtOfZeroes = numOfDigits - strlen( RightOf1 );
  for ( i = 0; i < amtOfZeroes; i++ ) {
    strcat( sP1, "0" );
  }
  strcpy( sP2, LeftOf2 );
  strcat( sP2, RightOf2 );
  amtOfZeroes = numOfDigits - strlen( RightOf2 );
  for ( i = 0; i < amtOfZeroes; i++ ) {
    strcat( sP2, "0" );
  }


  Z(n1);
  Z(n2);
  Z(res);

  mpz_set_str( n1, sP1, 10 );
  mpz_set_str( n2, sP2, 10 );
  mpz_add( res, n1, n2 );

  char * buff =  (char *) _alloca( mpz_sizeinbase( res, 10 ) + 2 + 1 );

  mpz_get_str(buff, 10, res);

  char * here = buff + strlen(buff) - numOfDigits; 

  memmove( here + 1, here, strlen(buff)); // plus trailing null
  *(here) = '.';

  BSTR bResult = _com_util::ConvertStringToBSTR( buff );
  return bResult;
}

나는 C가 조금 ... 음 ... 멍청한 일이라는 것을 인정한다. 모든 유용한 의견은 감사하게 받았습니다.

나는 FSUB와 FMUL도 구현하기 위해 여기에서 계속 갔다. FDIV는 거의 만족스럽지 않아 세 가지 버전으로 끝나고 합리적 숫자를 사용했습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top