Domanda

Nel codice seguente uso mpf_add per aggiungere la rappresentazione in forma di stringa di due valori fluttuanti. Ciò che non capisco a questo punto è perché 2.2 + 3.2 = 5.3999999999999999999999999999999999999999 . Avrei pensato che gmp fosse abbastanza intelligente da dare 5.4 .

Cosa non capisco su come gmp galleggia?

(A proposito, quando l'ho scritto per la prima volta non ero sicuro di come inserire un punto decimale, quindi la cifra delle cifre più / meno alla fine)

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;
}
È stato utile?

Soluzione

Ciò è dovuto all'errore intrinseco dell'uso dell'aritmetica in virgola mobile in un ambiente binario.

Vedi lo IEEE 754 per ulteriori informazioni.

Altri suggerimenti

Cosa warren detto .

Potresti ottenere risultati migliori se usi un decimale con codice binario invece di numeri in virgola mobile, anche se non posso davvero indirizzarti a nessuna libreria per quello.

Alla fine ho finito per rispondere da solo. La soluzione per me era fare nel codice quello che facevo a scuola. Il metodo funziona in questo modo:

  1. Prendi ogni numero e assicurati che il numero di cifre a destra del punto decimale sia lo stesso. Quindi, se aggiungi 2.1 e 3.457 , "normalizza" il primo a 2.100 . Tenere un registro del numero di cifre che si trovano a destra del decimale, in questo caso tre.
  2. Ora rimuovi il punto decimale e usa mpz_add per aggiungere i due numeri, che ora sono diventati 2100 e 3457 . Il risultato è 5557 .
  3. Infine, reinserisci il punto decimale a tre caratteri (in questo caso) da destra, fornendo la risposta corretta di 5.557 .

Ho prototipato la soluzione in VBScript (sotto)

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

e infine in Visual C ++ (sotto).

#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;
}

Accetto che la C sia un po '... beh ... schifosa, quindi non esitare a criticarla. Tutti i commenti utili ricevuti con gratitudine.

Sono passato da qui per implementare anche FSUB e FMUL. La FDIV non era così soddisfacente, finendo in tre versioni e usando numeri razionali.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top