0,1 float est supérieur à 0,1 double. Je m’attendais à ce que ce soit faux

Laisser:

double d = 0.1; float f = 0.1; 

devrait l’expression

 (f > d) 

retourne true ou false ?

Empiriquement, la réponse est true . Cependant, je m’attendais à ce que ce soit false .

Comme 0.1 ne peut pas être parfaitement représenté en binary, alors que double a 15 à 16 chiffres décimaux de précision, et que float n’a que 7 . Donc, ils sont tous deux inférieurs à 0.1 , tandis que le double est plus proche de 0.1 .

J’ai besoin d’une explication exacte pour le true .

Je dirais que la réponse dépend du mode d’arrondi lors de la conversion du double en float . float a 24 bits de précision et double 53. En binary, 0.1 est:

 0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂ ^ ^ ^ ^ 1 10 20 24 

Donc, si nous arrondissons au 24ème chiffre, nous aurons

 0.1₁₀ ~ 0.000110011001100110011001101 

ce qui est supérieur à la valeur exacte et à l’approximation plus précise à 53 chiffres.

Le nombre 0.1 sera arrondi à la représentation en virgule flottante la plus proche avec la précision donnée. Cette approximation peut être supérieure ou inférieure à 0,1, donc sans examiner les valeurs réelles, vous ne pouvez pas prédire si l’approximation à simple précision ou à double précision est plus grande.

Voici comment la valeur de la double précision est arrondie (à l’aide d’un interpréteur Python):

 >>> "%.55f" % 0.1 '0.1000000000000000055511151231257827021181583404541015625' 

Et voici la valeur de précision unique:

 >>> "%.55f" % numpy.float32("0.1") '0.1000000014901161193847656250000000000000000000000000000' 

Vous pouvez donc voir que l’approximation de précision simple est plus grande.

Si vous convertissez .1 en binary, vous obtenez:

  0.000110011001100110011001100110011001100110011001100 ... 

répéter pour toujours

Mapper aux types de données, vous obtenez:

 float (.1) =% .00011001100110011001101
                                      ^ --- note d'arrondissement
 double (.1) =% .0001100110011001100110011001100110011001100110011010

Convertissez cela en base 10:

 float (.1) = .10000002384185791015625
 double (.1) = .100000000000000088817841970012523233890533447265625

Cela a été tiré d’un article écrit par Bruce Dawson. Il peut être trouvé ici:
Les doubles ne sont pas des flotteurs, alors ne les comparez pas

Je pense que le commentaire d’Eric Lippert sur la question est en fait l’explication la plus claire, alors je vais la republier comme une réponse:

Supposons que vous calculez 1/9 en décimal à 3 chiffres et en décimal à 6 chiffres. 0.111 <0.111111, non?

Supposons maintenant que vous calculez 6/9. 0.667> 0.666667, non?

Vous ne pouvez pas l’avoir que 6/9 en décimal à trois chiffres est 0.666 parce que ce n’est pas le nombre décimal à 3 chiffres le plus proche de 6/9!

Comme il ne peut pas être exactement représenté, comparer 1/10 à la base 2 revient à comparer 1/7 à la base 10.

1/7 = 0,142857142857 … mais en comparant à différentes précisions de base 10 (3 contre 6 décimales), nous avons 0,143> 0,142857.

Juste pour append aux autres réponses en parlant de IEEE-754 et x86: le problème est encore plus compliqué qu’ils ne le semblent. Il n’y a pas “une” représentation de 0,1 dans IEEE-754 – il y en a deux. Soit arrondir le dernier chiffre vers le bas ou vers le haut serait valide. Cette différence peut et doit effectivement se produire , car x86 n’utilise pas les 64 bits pour ses calculs internes en virgule flottante; il utilise en fait 80 bits! Ceci s’appelle la double précision étendue .

Ainsi, même parmi les seuls compilateurs x86, il arrive parfois que le même nombre soit représenté de deux manières différentes, car certains calculent sa représentation binary avec 64 bits, tandis que d’autres utilisent 80.


En fait, cela peut arriver même avec le même compilateur, même sur la même machine!

 #include  #include  void foo(double x, double y) { if (std::cos(x) != std::cos(y)) { std::cout << "Huh?!?\n"; //← you might end up here when x == y!! } } int main() { foo(1.0, 1.0); return 0; } 

Voir Pourquoi cos(x) != cos(y) même si x == y ? pour plus d'informations.

Le rang de double est supérieur à celui de float dans les conversions. En faisant une comparaison logique, f est converti en double et l’implémentation que vous utilisez donne des résultats incohérents. Si vous ajoutez un suffixe f pour que le compilateur l’enregistre en tant que flottant, vous obtenez 0.00 qui est faux en double type. Les types flottants non mixés sont doubles.

 #include  #include  int main() { double d = 0.1; float f = 0.1f; printf("%f\n", (f > d)); return 0; }