2017-01-10 5 views
3

J'ai affaire à un code qui utilise de très petits nombres de l'ordre de 10^-15 à 10^-25. J'ai essayé d'utiliser double et long double mais je reçois une mauvaise réponse que soit 0.000000000000000000001 est arrondi à 0 ou un nombre comme 0.00000000000000002 est représenté par 0.00000000000000001999999999999.C++ très petit nombre à virgule flottante, preciscion

Puisque même une petite fraction de 1/1000000 fait une énorme différence dans mes réponses finales, existe-t-il une solution appropriée?

#include <iostream> 
#include <math.h> 
#include <stdlib.h> 
#include <iomanip> 

using namespace std; 

int main() 
{ 
    double sum, a, b, c,d; 
    a=1; 
    b=1*pow(10,-15); 
    c=2*pow(10,-14); 
    d=3*pow(10,-14); 
    sum=a+b+c+d; 
    cout<<fixed; 
    cout<<setprecision(30); 
    cout<<" a : "<<a<<endl<<" b : "<<b<<endl<<" c : "<<c<<endl 
     <<" d : "<<d<<endl; 
    cout<<" sum : "<<sum<<endl<<endl; 
    a=a/sum; 
    b=b/sum; 
    c=c/sum; 
    d=d/sum; 
    sum=a+b+c+d; 
    cout<<" a : "<<a<<endl<<" b : "<<b<<endl<<" c : "<<c<<endl 
     <<" d : "<<d<<endl; 
    cout<<" sum2: "<<sum<< endl; 
    return 0; 
} 

La sortie attendue devrait être:

a : 1.000000000000000000000000000000 
b : 0.000000000000001000000000000000 
c : 0.000000000000020000000000000000 
d : 0.000000000000030000000000000000 
sum : 1.000000000000051000000000000000 

a : 1.000000000000000000000000000000 
b : 0.000000000000001000000000000000 
c : 0.000000000000020000000000000000 
d : 0.000000000000030000000000000000 
sum1: 1.000000000000051000000000000000 

Mais, la sortie que je reçois est:

a : 1.000000000000000000000000000000 
b : 0.000000000000001000000000000000 
c : 0.000000000000020000000000000000 
d : 0.000000000000029999999999999998 
sum : 1.000000000000051100000000000000 

a : 0.999999999999998787999878998887 
b : 0.000000000000000999999997897899 
c : 0.000000000000019999999999999458 
d : 0.000000000000029999999999996589 
sum1: 0.999999999999989000000000000000 

J'ai essayé double, long double et même boost_dec_float, mais la sortie que je reçois sont similaire.

+0

Créez une structure qui est votre propre représentation du nombre; avec un raccourci pour l'exponentielle, et un double pour le reste; Ou peut-être changer les unités de sorte que vous ne travaillez pas avec de si petits nombres? – UKMonkey

+3

Je dirais que ce n'est pas un doublon. L'OP sait exactement ce qui se passe et demande une solution pour augmenter la précision. – SingerOfTheFall

+1

Donc, votre problème est que 3 pow (10, -14) est affiché comme 0.000000000000029999999999999998 au lieu de 0.000000000000030000000000000000. C'est à peu près sur les limites de la précision du double. –

Répondre

0

Comme vous l'avez remarqué, cela arrive parce que les nombres ne peuvent pas être représentés avec précision en binaire, et sont arrondis à un certain degré. Maintenant, depuis que vous l'avez marqué avec la balise boost, l'amélioration boost.multiprecision fait exactement ce que vous voulez. Il offre cpp_dec_float_50 type de données qui assure le calcul exact jusqu'à 50 chiffres décimaux. Il est utilisé comme tout autre type:

typedef boost::multiprecision::cpp_dec_float_50 value_type; 

value_type v1 = 1; 
value_type v2 = 3; 

value_type v3 = v1/v2; 

Selon stimuler doc, cela est garanti pour que seuls les bits précis:

cpp_dec_float_50 seventh = cpp_dec_float_50(1)/7; 
cpp_dec_float_50 circumference = boost::math::constants::pi<cpp_dec_float_50>() * 2 * seventh; 
std::cout.precision(std::numeric_limits<cpp_dec_float_50>::digits10); 
std::cout << circumference << std::endl; 
+0

Selon le but exact, les rationnels pourraient aussi être intéressants. –

0

Je parie que vous écrivez:

long double sum, a, b, c,d; 
a=1; 
b=1*pow(10,-15); 
c=2*pow(10,-14); 
d=3*pow(10,-14); 

Le problème est que le pow sera le double version de pow - pas la version longue double. Vous devez forcer l'un des arguments à long double:

long double sum, a, b, c,d; 
a=1; 
b=1*pow(10.0L,-15); 
c=2*pow(10.0L,-14); 
d=3*pow(10.0L,-14); 
+0

Cela peut acheter 5-6 chiffres décimaux supplémentaires de précision sur le matériel Intel. –