web-dev-qa-db-fra.com

0,1 flotteur 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)

retourner 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 binaire, alors que double a 15 à 16 chiffres décimaux de précision, et float n'a que 7. Donc, ils sont tous les 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.

99
Hesham Eraqi

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

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

Donc, si nous arrondissons haut au 24e chiffre, nous aurons

0.1₁₀ ~ 0.000110011001100110011001101

qui est supérieure à la valeur exacte et à l'approximation plus précise à 53 chiffres.

171
kennytm

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 regarder les valeurs réelles, vous ne pouvez pas prédire si l'approximation simple précision ou double précision est supérieure.

Voici à quoi la valeur de double précision est arrondie (en utilisant un 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 simple précision est plus grande.

55
Sven Marnach

Si vous convertissez .1 en binaire vous obtenez:

0,000110011001100110011001100110011001100110011001100 ...

répéter pour toujours

En mappant sur les types de données, vous obtenez:

 float (.1) =% .00011001100110011001101 
 ^ --- note arrondie 
 double (.1) =% .0001100110011001100110011001100110011001100110011010 

Convertissez cela en base 10:

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

Ceci est tiré d'un article écrit par Bruce Dawson. Il peut être trouvé ici:
Les doubles ne sont pas des flottants, alors ne les comparez pas

19
static_cast

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

Supposons que vous calculez 1/9 en décimal à 3 chiffres et 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 avoir que 6/9 en décimal à trois chiffres soit 0,666 car ce n'est pas la décimale à 3 chiffres la plus proche de 6/9!

5
Kip

Puisqu'il ne peut pas être représenté exactement, comparer 1/10 en base 2 revient à comparer 1/7 en 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.

4
xan

Le rang de double est supérieur à celui de float dans les conversions. En faisant une comparaison logique, f est converti en double et peut-être que l'implémentation que vous utilisez donne des résultats incohérents. Si vous suffixez f pour que le compilateur l'enregistre comme un flottant, alors vous obtenez 0,00 qui est faux en double type. Les types flottants non mélangés sont doubles.

#include <stdio.h>
#include <float.h>

int main()
{
     double d = 0.1;
     float f = 0.1f;
     printf("%f\n", (f > d));

     return 0;
}
1
Odimegwu David

Juste pour ajouter aux autres réponses en parlant d'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. L'arrondi du dernier chiffre vers le bas ou vers le haut serait valide. Cette différence peut et se produit réellement , car x86 ne fait pas utiliser 64 bits pour ses calculs internes en virgule flottante ; il utilise en fait 80 bits! Cela s'appelle double précision étendue .

Ainsi, même parmi les 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 binaire 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 <iostream>
#include <cmath>

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.