Comment un C # évalue-t-il la virgule flottante dans le survol et la fenêtre intermédiaire par rapport à la compilation?

StackOverflow https://stackoverflow.com/questions/1405486

  •  05-07-2019
  •  | 
  •  

Question

Je vois quelque chose d'étrange avec le stockage des doublons dans un dictionnaire, et je ne comprends pas pourquoi.

Voici le code:

            Dictionary<string, double> a = new Dictionary<string, double>();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

La deuxième instruction if fonctionne comme prévu. 1.0 n'est pas inférieur à 1.0. Maintenant, la première instruction if est évaluée comme étant vraie. Ce qui est très étrange, c’est que lorsque je survole le if, l’intellisense me dit faux, mais le code passe heureusement à Console.WriteLine.

Il s’agit de C # 3.5 dans Visual Studio 2008.

S'agit-il d'un problème de précision en virgule flottante? Alors pourquoi la deuxième instruction if fonctionne-t-elle? Je sens que je manque quelque chose de très fondamental ici.

Toute idée est appréciée.

Edit2 (Nouvelle question un peu reformulée):

Je peux accepter le problème de précision mathématique, mais ma question est la suivante: pourquoi le survol s’évalue-t-il correctement? Ceci est également vrai de la fenêtre intermédiaire. Je colle le code de la première instruction if dans la fenêtre intermédiaire et l’évaluation est fausse.

Mettre à jour

Tout d’abord, merci beaucoup pour toutes ces bonnes réponses.

J'ai également des problèmes pour le recréer dans un autre projet sur le même ordinateur. En regardant les paramètres du projet, je ne vois aucune différence. En regardant l’IL entre les projets, je ne vois aucune différence. En regardant le désassemblage, je ne vois aucune différence apparente (à part les adresses de mémoire). Pourtant, lorsque je débogue le projet d'origine, je vois: capture d'écran du problème

La fenêtre immédiate me dit que le if est faux, mais que le code tombe dans le conditionnel.

En tout état de cause, la meilleure solution, à mon avis, est de préparer l’arithmétique en virgule flottante dans ces situations. La raison pour laquelle je ne pouvais pas laisser passer cela a plus à voir avec les calculs du débogueur qui diffèrent de ceux de l'exécution. Merci beaucoup à Brian Gideon et à stephentyrone pour leurs commentaires très perspicaces.

Était-ce utile?

La solution

C’est un problème de précision flottante.

La deuxième instruction fonctionne car le compilateur compte l'expression 1e-3 * 1e3 avant d'émettre le fichier .exe.

Regardez dans ILDasm / Reflector, il émettra quelque chose comme

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");

Autres conseils

Le problème ici est assez subtil. Le compilateur C # n'émet pas (toujours) de code qui effectue le calcul en double, même lorsque c'est le type que vous avez spécifié. En particulier, il émet du code qui effectue le calcul de manière "étendue". précision en utilisant les instructions x87, sans arrondir les résultats intermédiaires pour doubler.

Selon que 1e-3 est évalué en tant que double ou long double et que la multiplication soit calculée en double ou long double, il est possible d'obtenir l'un des trois résultats suivants:

  • (long double) 1e-3 * 1e3 calculé en long double est égal à 1,0 - epsilon
  • (double) 1e-3 * 1e3 calculé en double est exactement 1,0
  • (double) 1e-3 * 1e3 calculé en long double vaut 1.0 + epsilon

De toute évidence, la première comparaison, celle qui ne répond pas à vos attentes, est évaluée de la manière décrite dans le troisième scénario que j'ai énuméré. 1e-3 est arrondi pour doubler, soit parce que vous le stockez et le chargez à nouveau, ce qui force l'arrondi, soit parce que C # reconnaît 1e-3 comme un littéral à double précision et le traite de cette façon. La multiplication est évaluée en long double car C # a un modèle numérique mortel , ainsi le compilateur génère le code.

Dans la deuxième comparaison, la multiplication est évaluée à l'aide de l'une des deux autres méthodes (vous pouvez déterminer laquelle en essayant "1> 1e-3 * 1e3"), ou bien le compilateur arrondit le résultat de la multiplication avant de la comparer à 1.0 lorsqu'elle évalue l'expression au moment de la compilation.

Il est probablement possible pour vous de dire au compilateur de ne pas utiliser la précision étendue sans le lui indiquer via certains paramètres de construction; l'activation de codegen vers SSE2 peut également fonctionner.

Voir les réponses ici

Euh… étrange. Je ne suis pas capable de reproduire votre problème. J'utilise également C # 3.5 et Visual Studio 2008. J'ai tapé dans votre exemple exactement tel qu'il a été posté et je ne vois pas non plus l'instruction Console.WriteLine exécuter.

De plus, la deuxième instruction if est optimisée par le compilateur. Lorsque j'examine les versions de débogage et de version dans ILDASM / Reflector, je ne vois aucune preuve de cela. Cela fait depuis parce que je reçois un avertissement du compilateur disant que du code inaccessible a été détecté.

Enfin, je ne vois pas comment cela pourrait de toute façon être un problème de précision en virgule flottante. Pourquoi le compilateur C # évaluerait-il statiquement deux doubles de manière statique par rapport au CLR au moment de l'exécution? Si tel était vraiment le cas, on pourrait alors soutenir que le compilateur C # a un bogue.

Modifier: Après y avoir réfléchi un peu plus, je suis encore plus convaincu qu'il ne s'agit pas d'un problème de précision en virgule flottante. Vous devez soit être tombé sur un bogue dans le compilateur ou le débogueur, sinon le code que vous avez publié n'est pas exactement représentatif de votre code en cours d'exécution. Je suis très sceptique à propos d'un bogue dans le compilateur, mais un bogue dans le débogueur semble plus probable. Essayez de reconstruire le projet et de le réexécuter. Peut-être que les informations de débogage compilées avec exe se sont désynchronisées ou quelque chose d'autre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top