9
votes

C # int stocké dans le double problème de précision "=="

Voici le code simplifié: xxx

est-il garanti que a == b est vrai ? < / p>


5 commentaires

Pourquoi pas? Je voudrais sûrement savoir pourquoi si quelqu'un dit non!


Parce que par exemple 4.0 / 2.0! = 8,0 / 4.0 Dans un cas général en raison de problèmes de précision. Il n'y a pas de calcul dans mon problème pour que la question soit si elle aide à éviter ce problème.


Bien que techniquement correct, il s'agit d'un mauvais choix, car ces deux valeurs peuvent être représentées exactement par un type de point de double flottant IEEE 754.


@codaizen, oui, tu as raison, je sais ça. C'est un exemple extrêmement simplifié.


(1.0 / 3.0) * 5.0! = 5.0 / 3.0 serait probablement un meilleur choix.


4 Réponses :


11
votes

Oui. Les entiers 32 bits peuvent être représentés exactement sous forme de chiffres de points flottants de 64 bits.


9 commentaires

Des références à la spécification, s'il vous plaît?


Je doute qu'il y ait quelque chose dans la spécification C # à propos de ce cas particulier, mais si vous regardez la spécification du format Binary64 IEEE 754, vous verrez qu'il utilise 52 bits pour encoder le signaland, ce qui signifie qu'il peut représenter des nombres entiers jusqu'à 2 ^ 52 - 1 exactement ... ou probablement plus que cela. Je ne suis pas tout à fait sûr.


Table de conversion numérique implicite (référence C #) ~ msdn.microsoft.com/en-us /Library/y5b434w4.aspx


En tout état de cause, même s'il y avait une erreur dans le calcul et i était une valeur "OFF", vous avez défini A et b à eux, ils ont donc la même valeur (potentiellement incorrecte). C'est comme demander si double A = 1; double b = a; , alors fait a == b ? Les erreurs de double précision peuvent être méchantes, mais pas comme ça. Même si vous convertissiez de long à double , et que vous avez perdu la précision, a et b aurait la fois le Même valeur "incorrecte" - trouver cela dans la spécification est peut-être une question plus intéressante.


@Nigel: Les conversions numériques implicites sont la chose qui nous permet d'écrire "Double A = 1" mais ne dit rien de précision.


@Levanovd: "Les conversions de l'INT, de l'UTINT ou de flotter et de long à doubler peuvent causer une perte de précision, mais pas une perte de grandeur." (du lien)


@Nigel Whatling de sorte que cela signifie qu'ils tournent vers + ou - infini plutôt que de tronquer des autres langues? En C ++ INT64_T (double (valeur)) si valeur a plus de 52 bits en utilisation et le jeu de bits le plus bas, la perte de précision provoque une perte de grandeur .


Wow, cela a été compliqué. Je ne pense pas que la citation de mon lien d'origine ait vraiment aidé (désolé @levanovd). Dans la langue C # Spec, cela va une autre: "Les autres conversions numériques implicites ne perdent jamais d'informations". Donc int to doubler ne perd rien. @Pete, je ne sais pas comment il se compare à C ++. Mais à la suite de l'exemple original, la valeur est une 32 bits int afin qu'elle n'aura jamais plus de 52 bits d'utilisation.


J'ai eu un badge à ce sujet qui me rappelait cette réponse de plus de cinq ans, et je voulais répondre à mon commentaire sur "Les erreurs de précision peuvent être méchantes, mais pas comme ça." Cela ne s'applique pas à C #, mais c'est une excellente lecture: GCC. gnu.org/bugzilla/show_bug.cgi?id=323#c109



-2
votes

J'ai ouvert Visual Studio, et je l'ai testé.

Voici mon code: P>

int i = 5;
double t = i;
double k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += 5;
t += 5;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += (int)Math.Round(5.6);
t += 5.6;
t = (int)Math.Round(t);
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = int.MaxValue - 5438;
t = int.MaxValue - 5438;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = (int)Math.Round(double.MaxValue);
t = Math.Round(double.MaxValue);
k = i;
MessageBox.Show((i == t).ToString()); //false
MessageBox.Show((k == t).ToString()); //false
i = (int)Math.Round(double.MaxValue);
t = i;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true


4 commentaires

J'ai bownvoché parce que votre conclusion me semble assez dangereuse (même si vous avez probablement raison): Juste parce que vous avez testé Un scénario simple Un scénario simple ne signifie pas que ce sera toujours travail. (Vous avez testé 1 cas sur 2 ^ 32. Vous manquez d'une preuve inductive pour montrer que votre conclusion est également valable pour tous les autres entiers.) - Dès que vous commencez à faire des calculs de point flottant, vous êtes probablement aller introduire des erreurs d'arrondi. Heureusement, pour les entiers, ceux-ci sont si petits qu'ils sont probablement insignifiants dans la plupart des cas.


Contre-exemple: Tout d'abord, remplacez les ajouts avec i / = 2; t / = 2; . Deuxièmement, rendez les distributeurs dans votre test d'égalité explicite: i == (int) t entraînera probablement toujours vrai , car les mauvaises erreurs d'arrondi à virgule flottante sont arrondies . Otoh, (double) i == t entraînera faux avec les divisions ci-dessus par 2. - Conclusion: pendant que vous pouvez probablement < I> Store Un entier dans un double sans perte de précision, ce n'est pas vrai dès que vous commencez à faire Computions Parce que vous introduisez des erreurs d'arrondi!


Avez-vous lu mon contre-exemple? Cela vous donne un cas où votre test ne fonctionnerait pas. Vos tests ne semblent fonctionner que parce que vous comptez sur la couche implicite de double à int pour variable t (et k < / code>). Mais: i et t sera souvent pas égal! Vous ne faites que "voir" l'égalité parce que vous arrondissez les petites différences! Essayez de comparer ces valeurs comme double s et voir ce qui se passe.


Désolé, je n'ai pas vu celui-là avant de poster mon édition. Vous avez raison. Mais la question des demandeurs était si les doubles seront égaux si elles obtiennent leurs valeurs du même Int, et elles le feront. Si vous effectuez des opérations sur l'int et sur le double après avoir défini leurs valeurs, elles ne seront plus égales, car le double peut stocker des nombres plus complexes.



3
votes

Oui, vous pouvez stocker un numéro d'entiers (32 bits) dans un double (numéro de point flottant 64 bits) sans perte de précision.

Cependant, dès que vous effectuez des calculs avec votre double , vous introduisez probablement des erreurs d'arrondi, c'est-à-dire une perte de précision. Ces erreurs seront probablement suffisamment petites afin qu'elles soient arrondies lorsque vous lancez votre Double retour sur int - mais l'erreur est là, alors en soyez conscient. < / p>

Comment ça se fait: voir Ce document ( IEEE Standard 754 Numéros de points flottants par Steve Hollasch) Pour plus de détails sur la manière dont un entier peut être stocké comme une valeur de point flottante.

Résumer (quelque peu inexactement), une valeur à virgule flottante se compose de trois parties: un bit de signalisation, une partie de "fraction" (appelée la mantissa) et une partie "exposant". Ils sont mis ensemble à peu près comme suit:

valeur = -1 bit de signe × fraction × 2 exponeent

Vous pouvez stocker la valeur entière dans la partie "fraction" du double (qui correspond à 52 bits larges, ce qui est plus large pour un entier 32 bits. La partie "Exponent" peut simplement être réglé sur 0, car ce n'est pas nécessaire.


0 commentaires

9
votes

est-il garanti qu'un == B est vrai?

Oui. En effet, vous effectuez la même conversion deux fois et que vous avez donné son comportement déterministe, vous vous retrouverez avec les mêmes valeurs, quels que soient les mêmes valeurs, quels que soient les problèmes d'arrondi.

Nous pouvons généraliser votre question cependant, pour:

Pouvons-nous effectuer des opérations arithmétiques sur des valeurs entières 32 bits codées dans double sans précision en vrac?

La réponse pour une telle question est également oui.

Une courte justification est que les opérations sur les bits de Mantissa (voir http://fr.wikipedia.org/wiki/significand ) sont précis si cela n'est possible que si cela est possible et en cas de valeurs entières 32 bits, il est possible.

L'histoire plus longue vient ici. Tant que votre valeur entière convient à 52 bits d'une partie de fraction appelée Mantissa (voir http: //fr.wikipedia .org / wiki / double_precision ) Tous les calculs sur les valeurs entière utilisant Double vont se comporter complètement OK.

Ceci est parce que votre numéro (disons 173 qui est 0000010101101b binaire) sera représenté comme 1.010110100000b * 2 ^ 7 , ce qui est précis.

Toutes les opérations sur MANTISSA sont directement en avant à mesure qu'elles correspondent à MANTUSSA. L'arrondi sur des entiers se produit lorsque le résultat d'une opération particulière ne correspond pas à la mantissa - par exemple. Vous multipliez 40 morceaux de MANTISSA par 40 bits de MANTISSA. L'arrondi sur les opérations de point flottant se produisent en outre lorsque des exposants sont bien différents. Dans ce cas, même une opération d'addition simple peut perdre de la précision, car les matissas sont décalés.

Retour aux entiers codés dans une double opération de division est précis, tant que le résultat est une valeur entière. Donc, 4.0 / 2.0 == 8.0 / 4.0 est également garanti d'être vrai.

Le problème commence lorsque votre numéro n'est pas entier. Mais même dans ce cas, les numéros sont garantis pour être représenté avec précision si elles sont une forme de x / 2 ^ y et x s'adapte dans 52 bits (par exemple, 3 / 4 5/8 345/1024 ). Les opérations de ces numéros sont également précises données y peuvent être égales pour les deux opérandes, de sorte que même: xxx

est garanti pour être vrai.

Fait intéressant est que vous pouvez effectuer une opération sur des entiers signés de 54 bits en toute sécurité. En effet, vous avez un bit supplémentaire au début dont la signification est codée par l'exposant et un bit supplémentaire pour un signe. Maintenant -2 ^ 53 Qui serait Min_int en cas d'entier signé de 54 bits ne correspond pas à la mantissa, mais l'exposant fera le travail ici avec Mantissa pleine de zéros.


0 commentaires