2010-06-23 6 views
2

J'ai une méthode qui traite de certaines coordonnées géographiques dans .NET, et j'ai une structure qui stocke une paire de coordonnées de sorte que si 256 est passé pour l'une des coordonnées, il devient 0. Cependant, dans un cas particulier, un valeur d'environ 255,99999998 est calculée, et donc stockée dans la structure. Quand il est imprimé dans ToString(), il devient 256, ce qui ne devrait pas arriver - 256 devrait être 0. Cela ne me dérangerait pas s'il imprimait 255.9999998 mais le fait qu'il imprime 256 quand le débogueur montre 255.99999998 est un problème. Avoir à la fois stocker et afficher 0 serait encore mieux.Comment contourner certaines erreurs d'arrondi?

Plus précisément, il y a un problème avec la comparaison. 255.99999998 est suffisamment proche de 256 tel qu'il devrait l'égaler. Que devrais-je faire en comparant les doubles? utiliser une sorte de valeur epsilon?


EDIT: Plus précisément, mon problème est que je prends une valeur, effectuer des calculs, puis effectuer les calculs opposés sur ce nombre, et je dois retourner la valeur d'origine exactement.

+0

L'utilisation d'epsilon est la seule méthode légale pour comparer des valeurs à virgule flottante. abs (a - b) <= epsilon – Andrey

+0

Il est parfaitement légal de comparer deux flottants avec ==, d'où le peu de compilateur qui s'en plaint. Cependant, la plus petite erreur d'arrondi peut faire que les deux valeurs ne soient pas égales quand les maths disent qu'elles devraient l'être, c'est pourquoi il n'est pas recommandé si vous appréciez votre santé mentale. – cHao

+0

Si vous avez juste besoin de récupérer exactement la valeur d'origine, ne pouvez-vous pas simplement la stocker, ou des informations sur le processus de calcul original avec le numéro? –

Répondre

1

Vous pouvez utiliser l'approche epsilon, mais l'epsilon est généralement un fudge pour contourner le fait que l'arithmétique à virgule flottante est à perte.

Vous pourriez envisager d'éviter complètement les points flottants binaires et utiliser une classe Rational sympa.

Le calcul ci-dessus était probablement destiné à 256 si vous faisiez de l'arithmétique sans perte comme vous le feriez avec un type Rational.

types Rational peuvent aller sous le nom de ratio ou de la classe Fraction, et sont assez simples à écrire

Voici un example. est ici another


Modifier ....

Pour comprendre votre problème considérer que lorsque la valeur décimale 0,01 est convertie en une représentation binaire, il ne peut pas être stocké dans la mémoire exactement finie. La représentation Hexidécimale pour cette valeur est 0.028F5C28F5C où le "28F5C" se répète à l'infini. Donc, même avant de faire des calculs, vous perdez l'exactitude juste en stockant 0,01 en format binaire.

Les classes Rational et Decimal sont utilisées pour résoudre ce problème, mais avec un coût de performance. Les types rationnels évitent ce problème en stockant un numérateur et un dénominateur pour représenter votre valeur. Le type décimal utilise un nombre décimal binaire codé format, qui peut être divisé par division, mais peut stocker exactement les valeurs décimales communes.

Pour votre usage, je suggère toujours un type Rational.

3

Cela ressemble à un problème avec la façon dont le numéro est imprimé, et non comment il est stocké. Un double a environ 15 chiffres significatifs, donc il peut dire 255.99999998 de 256 avec précision à épargner.

1

Vous pouvez choisir des chaînes de formatage qui devraient vous permettre d'afficher autant de chiffres que vous le souhaitez.

La façon habituelle de comparer les doubles pour l'égalité est de les soustraire et de voir si la valeur absolue est inférieure à un epsilon prédéfini, peut-être 0.000001.

+0

En utilisant le spécificateur de format R plutôt que le G par défaut, il affiche la valeur correcte. –

0

Vous devez vous décider sur un seuil sous lequel deux valeurs sont égales.Cela revient à utiliser ce qu'on appelle nombre de points fixes (par opposition à virgule flottante). Ensuite, vous devez effectuer l'arrondi manuellement.

Je voudrais aller avec un certain type non signé avec la taille connue (par exemple. Uint32 ou uint64 si elles sont disponibles, je ne sais pas .NET) et le traiter comme un mod de type de numéro de point fixe 256.

Par exemple.

typedef uint32 fixed; 

inline fixed to_fixed(double d) 
{ 
    return (fixed)(fmod(d, 256.) * (double)(1 << 24)) 
} 

inline double to_double(fixed f) 
{ 
    return (double)f/(double)(1 << 24); 
} 

ou quelque chose de plus élaboré pour convenir à une convention d'arrondissement (au plus proche, à la baisse, à la hausse, à étrange, à même). Les 8 bits les plus élevés de la partie fixe maintiennent la partie entière, les 24 bits inférieurs contiennent la partie fractionnaire. La précision absolue est 2^{- 24}.

Notez que l'addition et la soustraction de tels nombres se terminent naturellement autour de 256. Pour la multiplication, méfiez-vous.

Questions connexes