Ajouter des flotteurs avec gmp donne des résultats" corrects", en quelque sorte
dans le code ci-dessous, j'utilise mpf_add
pour ajouter la représentation en chaîne de deux valeurs flottantes. Ce que je ne comprends pas, c'est pourquoi 2.2 + 3.2 = 5.39999999999999999999999999999999999999
. J'aurais pensé que gmp
était assez intelligent pour donner 5.4
.
Qu'est-ce que je ne comprends pas sur la façon dont gmp flotte?
(BTW, quand j'ai écrit pour la première fois ceci je n'étais pas sûr Comment insérer un point décimal, donc le plus/moins chiffre trucs à la fin)
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;
}
3 réponses
vous pourriez avoir de meilleurs résultats si vous utilisez des nombres décimaux codés binaires au lieu de nombres à virgule flottante, bien que je ne puisse pas vraiment vous diriger vers des bibliothèques pour cela.
j'ai fini par répondre moi-même. La solution pour moi était de faire en code ce que je faisais à l'école. La méthode fonctionne comme ceci:
- prenez chaque numéro et assurez-vous que le nombre de chiffres à la droite du point décimal sont les mêmes. Donc, si vous ajoutez
2.1
et3.457
, "normalisez" le premier à2.100
. Gardez une trace du nombre de chiffres à droite du séparateur décimal, dans ce cas, trois. - maintenant supprimer le point décimal et utiliser
mpz_add
pour ajouter les deux nombres, qui sont devenus2100
et3457
. Le résultat est5557
. - enfin, réinsérer le point décimal trois caractères (dans ce cas) de la droite, donnant la réponse correcte de
5.557
.
je prototypage de la solution en VBScript (ci-dessous)
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
et enfin dans Visual C++ (ci-dessous).
#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 = '"151910920"';
} else {
*dotPos = '"151910920"';
strcpy( LeftOf1, sP1 );
strcpy( RightOf1, (dotPos + 1) );
}
dotPos = strstr( sP2, "." );
if ( dotPos == NULL ) {
strcpy( LeftOf2, sP2 );
*RightOf2 = '"151910920"';
} else {
*dotPos = '"151910920"';
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;
}
j'accepte que le C est un peu ... bien. .. dodgy, alors n'hésitez pas à le critiquer. Tous les commentaires utiles ont été reçus avec gratitude.
je suis allé d'ici à mettre en œuvre FSUB et FMUL ainsi. FDIV n'était pas aussi satisfaisant, se retrouvant en trois versions et utilisant des nombres rationnels.