0

Ma fonction pour se débarrasser d'une partie fractionnelle de deux doubles tout en conservant leur rapport:Comment surmonter l'imprécision de la double précision?

void enlarge(double &a, double &b) 
{ 
    while (a != trunc(a) || b != trunc(b)) 
    { 
     a *= 10; 
     b *= 10; 
     //output added for debugging 
     cout << "a = " << a << ", b = " << b << endl; 
     cout << "trunc(a) = " << trunc(a) << ", trunc(b) = " << trunc(b) << endl; 
     cout << "a == trunc(a): " << to_string(a == trunc(a)) << ", b == trunc(b): " << to_string(b == trunc(b)) << endl; 
     //to see output step by step 
     string asd; 
     cin >> asd; 
    } 
} 

sortie quand il arrête fonctionner correctement:

a = 0.876, b = 99.9 
trunc(a) = 0, trunc(b) = 99 
a == trunc(a): 0, b == trunc(b): 0 
a 
a = 8.76, b = 999 
trunc(a) = 8, trunc(b) = 999 
a == trunc(a): 0, b == trunc(b): 1 
a 
a = 87.6, b = 9990 
trunc(a) = 87, trunc(b) = 9990 
a == trunc(a): 0, b == trunc(b): 1 
a 
a = 876, b = 99900 
trunc(a) = 876, trunc(b) = 99900 //This is where it stops working 
a == trunc(a): 0, b == trunc(b): 1 //Notice how 876 != 876 
a 
a = 8760, b = 999000 
trunc(a) = 8760, trunc(b) = 999000 
a == trunc(a): 0, b == trunc(b): 1 

Que dois-je faire dans cette situation?

+0

A) Il est plutôt difficile de trouver des bogues dans le code que vous ne montrez pas. B) std :: cout n'imprime pas les nombres à leur pleine précision. Il y a plusieurs questions ici sur ce sujet. Il suffit de le chercher. Si vous vous souciez du format de sortie, j'utiliserais printf. C) Pour répondre à votre question: Utilisez un type de numéro plus précis. – user463035818

+0

Jetez un coup d'oeil à ceci: http://stackoverflow.com/questions/10334688/how-dangerous-is-it-to-compare-floating-point-values ​​ – NathanOliver

+1

Avez-vous envisagé d'utiliser le calcul * point fixe *? Vous pourriez être en mesure d'obtenir une meilleure précision. –

Répondre

1

Le problème est que le numéro d'origine n'était pas exactement .876, même si ce n'était pas très différent. Donc, vous devrez peut-être multiplier par 10 plusieurs fois pour obtenir un nombre entier.

Notez également que si le nombre était légèrement plus petit que 0,876, 1000 fois le nombre serait légèrement inférieur à 876,0 et trunc (875,99999999 ...) est de 875, et non 876.

Vous pourriez envisager un certain valeur de seuil où le nombre est "assez proche" d'un nombre entier. par exemple, au lieu d'utiliser a==trunc(a), vous pouvez utiliser le test

abs(a - round(a)) < 1e-6 

Enfin, si vous voulez être précis, vous pouvez multiplier par 2 à chaque étape, au lieu de par 10. Vous perdez un peu de précision à chaque fois vous multipliez par 10, car 10 n'est pas une puissance de 2. Multiplier par 2 produira un nombre entier après un maximum de 52 multiplications.

+0

Pourquoi s'embêter avec 'double', si vous pouviez simplement utiliser _fixed point math_? –

+0

@ πάνταῥεῖ: Si je pouvais utiliser un point fixe * simplement *, je l'utiliserais probablement plus souvent, mais il est seulement simple à utiliser dans des domaines très restreints, même avec des bibliothèques externes. Dans le lien que vous fournissez, Fowler se plaint du manque de support de devises dans les bibliothèques standard, par exemple. Donc, je me soucie de «double» quand je ne peux pas être dérangé pour construire un environnement à virgule fixe :) – rici

1

« Ma fonction se débarrasser d'une partie fractionnelle de deux doubles tout en conservant leur rapport: »

Vous voulez probablement utiliser des valeurs de point fixe, au lieu de double du tout , pour que cela fonctionne correctement. Quelque chose comme décrit dans le Money Pattern, qui a été conçu pour surmonter ce genre de problèmes.

L'idée générale est que vous avez une valeur int qui le maintient par une représentation (interne) multipliée en fonction de la précision que vous voulez/avez besoin. Donc, si vous avez besoin d'une précision de 3 points décimaux, vous multipliez cette valeur (en interne) avec 1000, 4 points décimaux -> * 10000, aso.

Si vous devez gérer des valeurs énormes qui ne peuvent pas être conservées (représentées sous) comme une variable entière long long, vous pouvez également utiliser des bibliothèques de gestion de grands entiers (non standard).