Hinzufügen von Schwimmern mit gmp gibt „richtige“ Ergebnisse, Art
-
05-07-2019 - |
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;
}
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
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:
- jede Zahl Nehmen und stellen Sie sicher, dass die Anzahl der Ziffern rechts vom Komma gleich ist. Also, wenn das Hinzufügen
2.1
und3.457
, ‚normalisiert‘ die erste2.100
. Notieren Sie die Anzahl der Ziffern, die rechts von der Dezimalzahl sind, in diesem Fall drei. - Geben Sie nun das Komma entfernen und verwenden
mpz_add
die beiden Zahlen zu addieren, die jetzt2100
und3457
geworden sind. Das Ergebnis ist5557
. - 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.