7 Réponses :



1
votes

Les comparaisons de nombres de double précision sont intrinsèquement inexactes. Par exemple, vous pouvez souvent trouver 0.0 == 0.0 retour false . Cela est dû à la manière dont la FPU stocke et suit les numéros.

Wikipedia dit :

Les tests d'égalité sont problématiques. Deux séquences informatiques qui sont mathématiquement égales peuvent produire des valeurs à virgule flottante différentes.

Vous devrez utiliser un delta pour donner une tolérance à vos comparaisons, plutôt qu'une valeur exacte.


0 commentaires

2
votes

Ce peut être que dans l'un des cas, vous comprenez de comparer un double enregistreur interne à 64 bits à un registre interne 80 bits. Il peut être éclairant de regarder les instructions de montage émet des éditions GCC pour les deux cas ...


0 commentaires

12
votes

C'est quelque chose qui m'a mordu, aussi.

Oui, les numéros de points flottants ne doivent jamais être comparés à l'égalité en raison d'une erreur d'arrondi, et vous saviez probablement cela.

mais dans ce cas, Vous êtes informatique t1 + t2 , puis le calculant à nouveau. sûrement qui doit produire un résultat identique?

Voici ce qui se passe probablement. Je vais parier que vous exécutez ceci sur un processeur X86, correct? Le X86 FPU utilise 80 bits pour ses registres internes, mais les valeurs de la mémoire sont stockées sous forme de double 64 bits.

donc t1 + t2 est d'abord calculé avec 80 bits de précision, Ensuite, je présume - stocké - stocké à la mémoire dans Sum_2 avec 64 bits de précision - et une certaine arrondi se produit. Pour l'affirmation, elle est chargée dans un registre de point flottant et t1 + t2 est à nouveau calculé, à nouveau avec 80 bits de précision. Alors maintenant, vous comparez sum_2 , qui a déjà été arrondi sur une valeur de point flottante de 64 bits, avec t1 + t2 , calculé avec une précision supérieure (80 bits) - Et c'est pourquoi les valeurs ne sont pas exactement identiques.

éditer alors pourquoi le premier test de test? Dans ce cas, le compilateur évalue probablement 4.0 + 6,3 au moment de la compilation et le stocke comme une quantité de 64 bits - à la fois pour l'affectation et pour l'affirmation. Les valeurs donc identiques sont comparées et les affirmations passent.

secondaire (em> voici le code de montage généré pour la deuxième partie du code (GCC, X86), avec des commentaires - Suit à peu près le scénario décrit ci-dessus: xxx

côté intéressant Remarque: ceci a été compilé sans optimisation. Quand il est compilé avec -O3 , le compilateur optimise tous du code.


5 commentaires

Je ne pense pas que c'est même ça. La chose est que 4.0 + 6,3 est une expression qui est pliée constante par le compilateur à 10.3. Donc, la première affirmation devient équivalente à Assert (10.3 == 10.3) qui passe de manière triviale. Dans le deuxième test, cela prend réellement de 4,0, le mettre en double, prendre 6,3, mettez-le dans un double (perte d'un peu de précision), ajoutez-les ensemble et comparez que constante 10.3 , qui échoue parce que c'est différent de 2 ^ -70 environ. :)


Je ne suis pas convaincu ... Si le compilateur peut optimiser T1 + T2 à 10.3 dans la déclaration d'assertion, pourquoi ne peut-il pas faire la même optimisation dans l'affectation à Sum_2?


En regardant la liste, tu m'as eu. :) Pauvre deviner de ma part, je suppose.


Merci pour la réponse géniale! Vous avez raison - je cours sur un X86 et je savais que je ne devrais pas comparer des flotteurs en général, mais c'était dans un test unitaire en veillant à ce que certaines opérations matricielles fonctionnaient correctement. Il passe bien lorsqu'il est compilé avec Visual Studio ... Oh bien.


C'est exactement la situation dans laquelle j'ai rencontré cela - dans un test unitaire dans un cas où je pensais pouvoir exiger une égalité exacte.



3
votes

J'ai dupliqué votre problème sur mon duo Intel Core 2, et j'ai examiné le code de montage. Voici ce qui se passe: lorsque votre compilateur évalue t1 + t2 , il fait xxx

quand il stocke dans sommate2 il fait xxx

alors la comparaison == compare la somme de 80 bits à une somme de 64 bits, et ils sont différents, principalement parce que la partie fractionnelle 0.3 ne peut pas être Représenté avec exactement à l'aide d'un numéro de point flottant binaire, vous comparez donc une "décimale répétée" (répétant réellement binaire) qui a été tronquée à deux longueurs différentes.

Qu'est-ce qui est vraiment irritant, c'est que si vous irritant, c'est que si vous êtes vraiment irritant, c'est que si votre compilateur avec < Code> GCC -O1 ou GCC -O2 , GCC fait le mauvais arithmétique à la compilation, et le problème disparaît. Peut-être que cela va bien selon la norme, mais c'est juste une autre raison que GCC n'est pas mon compilateur préféré.


P.S. Quand je dis que == compare une somme de 80 bits avec une somme de 64 bits, bien sûr, je veux vraiment dire que cela compare la version étendue de la somme 64 bits. Vous risquez de bien vouloir penser xxx

se résout à xxx

et xxx

résout à xxx

Bienvenue dans le monde merveilleux du point flottant!


0 commentaires

3
votes

Lors de la comparaison des nombres de points flottants pour la proximité, vous souhaitez généralement mesurer leur différence relative, qui est définie comme xxx

par exemple, xxx < p> L'idée est de mesurer le nombre de chiffres importants dont les chiffres ont en commun; Si vous prenez le -log10 de 0.000195787019, vous obtenez le 3.70821611, qui concerne le nombre de basses principales 10 chiffres que tous les exemples ont en commun.

Si vous devez déterminer si deux numéros de points flottants sont égaux, vous devez Faites quelque chose comme xxx

où Machine Epsilon est le plus petit nombre qui peut être maintenu dans la mantissa du matériel à virgule flottante utilisé. La plupart des langues informatiques ont une fonction d'appel pour obtenir cette valeur. ERROR_Factor devrait être basé sur le nombre de chiffres significatifs que vous pensez être consommé par des erreurs d'arrondi (et des autres) dans les calculs des nombres x et y. Par exemple, si je savais que X et Y étaient le résultat d'environ 1 000 sommations et que vous ne connaissiez aucune limite sur les chiffres en cours de sommation, je définirais Errest_Factor sur environ 100.

essayé de les ajouter comme des liens Mais ne pouvait-il pas puisque c'est mon premier post:

  • en.wikipedia.org/wiki/relative_difefence
  • en.wikipedia.org/wiki/machine_epsilon
  • en.wikipedia.org/wiki/Signific et (MANTISSA)
  • fr.wikipedia.org/wiki/rounding_error

0 commentaires

0
votes

Ce "problème" peut être "corrigé" en utilisant ces options:

-MSE2 -MFPMATH = SSE

Comme expliqué sur cette page:

http://www.network-theory.co.uk /docs/gccinttro/gccintro_70.html

Une fois que j'ai utilisé ces options, les deux affirmations passées.


0 commentaires