J'ai le code simple suivant :
int speed1 = (int)(6.2f * 10);
float tmp = 6.2f * 10;
int speed2 = (int)tmp;
speed1
et speed2
devraient avoir la même valeur, mais en fait, j'ai :
speed1 = 61
speed2 = 62
Je sais que je devrais probablement utiliser Math.Round au lieu du casting, mais j'aimerais comprendre pourquoi les valeurs sont différentes.
J'ai regardé le bytecode généré, mais à part un store et un load, les opcodes sont les mêmes.
J'ai également essayé le même code en java, et j'obtiens correctement 62 et 62.
Quelqu'un peut-il expliquer cela ?
Edit : Dans le code réel, ce n'est pas directement 6.2f * 10 mais un appel de fonction * une constante. J'ai le bytecode suivant :
pour speed1
:
IL_01b3: ldloc.s V_8
IL_01b5: callvirt instance float32 myPackage.MyClass::getSpeed()
IL_01ba: ldc.r4 10.
IL_01bf: mul
IL_01c0: conv.i4
IL_01c1: stloc.s V_9
pour speed2
:
IL_01c3: ldloc.s V_8
IL_01c5: callvirt instance float32 myPackage.MyClass::getSpeed()
IL_01ca: ldc.r4 10.
IL_01cf: mul
IL_01d0: stloc.s V_10
IL_01d2: ldloc.s V_10
IL_01d4: conv.i4
IL_01d5: stloc.s V_11
nous pouvons voir que les opérandes sont des flottants et que la seule différence est le stloc/ldloc
.
Quant à la machine virtuelle, j'ai essayé avec Mono/Win7, Mono/MacOS et .NET/Windows, avec les mêmes résultats.
9 votes
Je pense que l'une des opérations a été effectuée en simple précision alors que l'autre a été effectuée en double précision. L'une d'entre elles a renvoyé une valeur légèrement inférieure à 62, ce qui donne 61 lors de la troncature en entier.
2 votes
Il s'agit de problèmes typiques de la précision des points flottants.
0 votes
Et pour faire bonne mesure
(int)(6.2d * 10)
retourne également62
ce qui soutiendrait (en quelque sorte) ce que @Gabe a suggéré.0 votes
Y a-t-il une raison pour laquelle vous effectuez une conversion en int au lieu d'une analyse syntaxique ? int speed1 = (int)(6.2f * 10) se lirait alors int speed1 = Int.Parse(6.2f * 10) ; la différence est probablement due à l'arrondi, si vous effectuez une conversion en double vous obtiendrez probablement quelque chose comme 61.78426
0 votes
Convert.ToInt32(6.2f * 10)
est aussi de 62 ans.0 votes
(int)(6.2d * 10)
retourne 62.3 votes
En essayant sur .Net/WinXP, .Net/Win7, Mono/Ubuntu et Mono/OSX, vous obtenez vos résultats pour les deux versions Windows, mais 62 pour speed1 et speed2 dans les deux versions Mono. Merci @BoltClock
0 votes
@Ken2k peut-être, parce que (6.2f * 10) est toujours un flottant, invoquant ainsi
Convert.ToInt32(float)
qui présente (probablement) le même comportement que l'utilisation d'un temporaire.6 votes
M. Lippert... vous êtes dans le coin ??
6 votes
L'évaluateur d'expressions constantes du compilateur ne gagne aucun prix ici. Il est clair qu'il tronque 6.2f dans la première expression, il n'a pas de représentation exacte en base 2 et finit donc par 6.199999. Mais il ne le fait pas dans la deuxième expression, probablement en réussissant à le garder en double précision d'une manière ou d'une autre. C'est une autre façon de faire, la cohérence de la virgule flottante n'est jamais un problème. Cela ne va pas être corrigé, vous connaissez la solution de contournement.
0 votes
Voir aussi stackoverflow.com/questions/6683059/