Frage

In dem folgenden Code I mpf_add verwenden, um die String-Darstellung von zwei schwimmenden Werten hinzuzufügen. Was ich an dieser Stelle nicht verstehe ist, warum 2.2 + 3.2 = 5.39999999999999999999999999999999999999. Ich hätte gedacht, dass gmp klug genug war 5.4 zu geben.

Was soll ich nicht verstehen, wie gmp tut schwimmt?

(BTW, wenn ich zum ersten Mal das schrieb, war ich nicht sicher, wie ein Komma einzufügen, so dass die Plus / Minus-digit Sachen am Ende)

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;
}
War es hilfreich?

Lösung

Dies ist wegen des inhärenten Fehlers der Verwendung von Gleitkommaarithmetik in einer binären Umgebung.

Siehe IEEE 754 Standard für weitere Informationen.

Andere Tipps

Was warren sagte .

Sie können bessere Ergebnisse, wenn Sie binär codierte Dezimalzahl anstelle von Gleitkommazahlen, obwohl ich nicht wirklich Sie zu irgendwelchen Bibliotheken für das richten kann.

Ich landete schließlich auf diese selbst zu beantworten. Die Lösung war für mich in Code zu tun, was ich früher in der Schule zu tun. Das Verfahren funktioniert wie folgt:

  1. jede Zahl Nehmen und stellen Sie sicher, dass die Anzahl der Ziffern rechts vom Komma gleich ist. Also, wenn das Hinzufügen 2.1 und 3.457, ‚normalisiert‘ die erste 2.100. Notieren Sie die Anzahl der Ziffern, die rechts von der Dezimalzahl sind, in diesem Fall drei.
  2. Geben Sie nun das Komma entfernen und verwenden mpz_add die beiden Zahlen zu addieren, die jetzt 2100 und 3457 geworden sind. Das Ergebnis ist 5557.
  3. Schließlich legen Sie das Komma drei Zeichen (in diesem Fall) von der rechten Seite, die richtige Antwort von 5.557 geben.

I prototypisiert die Lösung in VBScript (unten)

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

und schließlich in Visual C ++ (siehe unten).

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

Ich akzeptiere, dass die C ist ein bisschen ... na ja ... vertrackt, so bitte er zur Kritik frei fühlen. Alle hilfreichen Kommentare erhalten dankbar.

ging ich von hier auf FSUB und FMUL auch umzusetzen. FDIV war bei weitem nicht so befriedigend, in drei Versionen enden und mit rationalen Zahlen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top