2011-11-09 6 views
7

Je suis tombé sur l'exemple suivant sur wikipedia (http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion).Conversion de type implicite en C

#include <stdio.h> 

int main() 
{ 
    int i_value = 16777217; 
    float f_value = 16777217.0; 
    printf("The integer is: %i\n", i_value); // 16777217 
    printf("The float is: %f\n", f_value); // 16777216.000000 
    printf("Their equality: %i\n", i_value == f_value); // result is 0 
} 

Leur explication: « Ce comportement étrange est causée par une distribution implicite de i_value flotter lorsqu'il est comparé à f_value, un casting qui perd la précision, ce qui rend les valeurs comparées différentes. »

N'est-ce pas? Si i_value était lancé pour flotter, les deux auraient la même perte de précision et ils seraient égaux. Donc i_value doit être casté en double.

+0

Avec g ++ (GCC 4.6.2), j'obtiens '1' pour l'égalité. –

+0

@Kerrek: Et moi. En VS, je reçois 0. –

+0

@OliCharlesworth: Je suis curieux de changer le littéral à 'f' ou le type à' double' - je reçois '1' dans tous les cas ... –

Répondre

7

Non, dans le cas de l'opérateur d'égalité, les « conversions arithmétiques habituelles » se produisent, qui commencent:

  • Tout d'abord, si le soit opérande type réel correspondant est long double, la l'autre opérande est converti, sans changement de type , en un type dont le type réel correspondant est long double.
  • Sinon, si le type réel correspondant de l'un ou l'autre opérande est double, l'autre opérande est converti, sans changement de type , en un type dont le type réel correspondant est double.
  • Sinon, si le type réel correspondant de l'un ou l'autre opérande est float, l'autre opérande est converti, sans changement de type , en un type dont le type réel correspondant est float.

Ce dernier cas s'applique ici: i_value est converti en float.

La raison pour laquelle vous pouvez voir un résultat bizarre de la comparaison, malgré cela, est à cause de cette mise en garde aux conversions arithmétiques habituelles:

Les valeurs des opérandes flottantes et des résultats de flottant les expressions peuvent être représentées avec une plus grande précision et portée que que requis par le type; les types ne sont pas modifiés de cette manière.

C'est ce qui se passe: le type de converti i_value est encore float, mais dans cette expression de votre compilateur profite de cette latitude et représenter plus de précision que float. Il s'agit d'un comportement de compilateur typique lors de la compilation pour un virgule flottante compatible avec 387, car le compilateur laisse des valeurs temporaires sur la pile à virgule flottante, qui stocke des nombres à virgule flottante dans un format de précision étendu de 80 bits.

Si votre compilateur est gcc, vous pouvez désactiver cette précision supplémentaire en donnant l'option de ligne de commande -ffloat-store.

+0

Sur x64 gcc utilise une instruction explicite cvtsi2ssl qui convertit un entier en flottant. Sur le x86, cependant, c'est exactement ce qui se passe et une plus grande précision est même plus que doublée. –

+0

@ konrad.kruczynski: Oui, et vous pouvez obtenir le même résultat sur x86 en fournissant l'option '-mfpmath = sse' (qui requiert également' -msse' ou une option qui l'implique). – caf

-1

Je crois que la plus grande valeur entière qu'un point flottant IEEE 32 bits peut contenir est 1048576 qui est plus petit que le nombre ci-dessus. Donc, il est certainement vrai que la valeur à virgule flottante ne tiendra pas exactement 16777217.

La partie dont je ne suis pas sûr est la façon dont le compilateur fait la comparaison entre deux types de nombres différents (ie un float et un int) . Je pense à trois différentes façons cela peut être fait:

1) Convertir les deux valeurs à « flotter » (cela devrait rendre les valeurs même, il est sans doute pas ce que le compilateur ne)

2) Convertir les deux valeurs à "int" (cela peut ou ne peut pas les montrer la même chose ... convertir en un int tronque souvent, donc si la valeur à virgule flottante est 16777216.99999, alors convertir en un "int" tronquer)

3) Convertir les deux valeurs en "double". Ma conjecture serait que c'est ce que le compilateur ferait. Si c'est ce que fait le compilateur, les deux valeurs seraient certainement différentes. Un double peut contenir 16777217 exactement, et il pourrait également représenter exactement la valeur du point flottant que 16777217.0 convertit en (ce qui n'est pas exactement 16777217.0).

+3

1048576 est 2^20, donc c'est incorrect. –

0

Il y a quelques bonnes réponses ici. Vous devez être très prudent lors de la conversion entre différents entiers et diverses représentations à virgule flottante.

En général, je ne teste pas les nombres à virgule flottante pour l'égalité, en particulier si l'un d'entre eux provient d'une distribution implicite ou explicite d'un type entier. Je travaille sur une application qui est pleine de calculs géométriques. Autant que possible, nous travaillons avec des entiers normalisés (en forçant une précision maximale que nous accepterons dans les données d'entrée). Pour les cas où vous devez utiliser le point flottant, nous appliquerons une valeur absolue à la différence si une comparaison est nécessaire.

Questions connexes