sortie étrange en comparaison du flotteur avec flotteur littéral
-
12-09-2019 - |
Question
float f = 0.7;
if( f == 0.7 )
printf("equal");
else
printf("not equal");
Pourquoi le not equal
de sortie?
Pourquoi cela?
La solution
Cela se produit parce que dans votre déclaration
if(f == 0.7)
0,7 est considérée comme une double. Essayez 0.7f pour assurer la valeur est traitée comme un flotteur:
if(f == 0.7f)
Mais comme Michael a suggéré dans les commentaires ci-dessous vous ne devriez jamais tester l'égalité exacte des valeurs à virgule flottante.
Autres conseils
Cette réponse pour compléter ceux qui existent déjà: noter que 0,7 est représentable pas exactement soit comme un flotteur (ou double). Si elle était représentée exactement, alors il n'y aurait pas de perte d'information lors de la conversion de flotter, puis de nouveau doubler, et vous n'auriez pas ce problème.
On pourrait même dire qu'il devrait y avoir un compilateur d'avertissement pour les constantes en virgule flottante littéral qui ne peut pas être représenté exactement, en particulier lorsque la norme est si floue quant à savoir si l'arrondi sera effectué au moment de l'exécution dans le mode qui a été fixé comme temps que ou au moment de la compilation dans un autre mode d'arrondi.
Tous les nombres non entiers qui peuvent être représentés exactement ont 5
que leur dernier chiffre décimal. Malheureusement, l'inverse est pas vrai: certains chiffres ont 5
leur dernier chiffre décimal et ne peuvent pas être représentés exactement. Les petits entiers peuvent tous être représentés exactement, et la division par une puissance de 2 transforme un nombre qui peut être représenté dans un autre qui peut être représenté, tant que vous ne saisissez pas le royaume des nombres dénormalisés.
Tout d'abord laisser regarder à l'intérieur numéro de flotteur. Je prends 0.1f est de 4 octets de long (binary32), dans l'hexagone, il est
3D CC CC CD .
par le standart IEEE 754 pour le convertir en décimal que nous devons faire comme ceci:
En 3D binaire CC CC CD est
0 01111011 1001100 11001100 11001101
ici premier chiffre est un bit de signe. 0 signifie (-1) ^ 0 que notre nombre est positif.
Deuxième 8 bits est un Exponent. En binaire, il est 01111011 - en décimal 123. Mais le vrai Exponent est 123-127 (toujours 127) = -4 , il est à dire que nous devons multiplier le nombre que nous obtiendrons par 2 ^ (- 4 ).
Les 23 derniers octets est la précision Signifiant. Là, le premier bit on multiplie par 1 / (2 ^ 1) (0,5), d'autre part par 1 / (2 ^ 2) (0,25) et ainsi de suite. Voici ce que nous obtenons:
Nous devons ajouter tous les nombres (puissance de 2) et y ajouter 1 (toujours 1, par Standart). Il est
1,60000002384185791015625
Maintenant, nous allons multiplier ce nombre par 2 ^ (- 4), il est de Exponent. Nous comptons simplement Devide ci-dessus par 2 quatre temps:
0,100000001490116119384765625
Je MS Calculator
**
Maintenant la deuxième partie. Conversion de décimal en binaire.
**
Je prends le numéro 0.1
Il facilité parce qu'il n'y a pas de partie entière. bit premier signe - il est 0.
Et Exponent Signifiant précision je calculer maintenant. La logique est multiplié par 2 nombre entier (0,1 * 2 = 0,2) et si elle est plus grand que 1 et de soustraction continue.
Et le nombre est ,00011001100110011001100110011, Standart dit que nous devons passer à gauche avant d'arriver 1. (quelque chose). Comment vous voyez nous avons besoin de 4 quarts de travail, de ce nombre calcul Exponent (127-4 = 123 ). Et la précision Signifiant est maintenant
10011001100110011001100 (et il est perdu bits).
Maintenant, le nombre entier. bit de signe 0 Exponent est 123 ( 01111011 ) et Signifiant précision est 10011001100110011001100 et il est tout
00111101110011001100110011001100
comparons avec ceux que nous avons du chapitre précédent
00111101110011001100110011001101
Comme vous le voyez le bit dure ne sont pas égaux. Il est parce que je tronque le nombre. La CPU et le compilateur savent que la précision est quelque chose après Signifiant ne peut pas tenir et mettre juste le dernier bit à 1.
Le problème que vous êtes confronté est, comme d'autres commentateurs l'ont noté, qu'il est généralement dangereux pour tester l'équivalence exacte entre les flotteurs, les erreurs d'initialisation ou des erreurs d'arrondi dans les calculs peuvent présenter des différences mineures qui causeront l'opérateur == pour return false.
Une meilleure pratique consiste à faire quelque chose comme
float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
printf("equal");
else
printf("not equal");
En supposant que FLT_EPSILON a été définie comme une valeur flottante appropriée pour votre petite plate-forme.
Étant donné que les erreurs d'arrondi ou l'initialisation est peu susceptible de dépasser la valeur de FLT_EPSILON, cela vous donnera le test d'équivalence fiable que vous recherchez.
Beaucoup de réponses sur le web font l'erreur de regarder la différence abosulute entre nombres à virgule flottante, ceci est valable uniquement pour les cas particuliers, la façon robuste est de regarder la différence relative comme ci-dessous:
// Floating point comparison:
bool CheckFP32Equal(float referenceValue, float value)
{
const float fp32_epsilon = float(1E-7);
float abs_diff = std::abs(referenceValue - value);
// Both identical zero is a special case
if( referenceValue==0.0f && value == 0.0f)
return true;
float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) );
if(rel_diff < fp32_epsilon)
return true;
else
return false;
}
Une autre question exacte près était liée à celle-ci ainsi la réponse tardive des années. Je ne pense pas que les réponses ci-dessus sont complètes.
int fun1 ( void )
{
float x=0.7;
if(x==0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=1.1;
if(x==1.1) return(1);
else return(0);
}
int fun3 ( void )
{
float x=1.0;
if(x==1.0) return(1);
else return(0);
}
int fun4 ( void )
{
float x=0.0;
if(x==0.0) return(1);
else return(0);
}
int fun5 ( void )
{
float x=0.7;
if(x==0.7f) return(1);
else return(0);
}
float fun10 ( void )
{
return(0.7);
}
double fun11 ( void )
{
return(0.7);
}
float fun12 ( void )
{
return(1.0);
}
double fun13 ( void )
{
return(1.0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00000 mov r0, #0
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
00000010 <fun3>:
10: e3a00001 mov r0, #1
14: e12fff1e bx lr
00000018 <fun4>:
18: e3a00001 mov r0, #1
1c: e12fff1e bx lr
00000020 <fun5>:
20: e3a00001 mov r0, #1
24: e12fff1e bx lr
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
Pourquoi est-ce fun3 et fun4 retourner l'un et pas les autres? Pourquoi fonctionne-t-fun5?
Il est de la langue. La langue dit que 0,7 est une double sauf si vous utilisez cette syntaxe 0.7f alors il est un seul. Donc,
float x=0.7;
la double 0,7 est convertie en un seul et stocké dans x.
if(x==0.7) return(1);
La langue dit que nous devons promouvoir la plus grande précision de sorte que la seule en x est converti en un double et par rapport à la double 0,7.
00000028 <fun10>:
28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8>
2c: e12fff1e bx lr
30: 3f333333 svccc 0x00333333
00000034 <fun11>:
34: e28f1004 add r1, pc, #4
38: e8910003 ldm r1, {r0, r1}
3c: e12fff1e bx lr
40: 66666666 strbtvs r6, [r6], -r6, ror #12
44: 3fe66666 svccc 0x00e66666
simple 3f333333 deux 3fe6666666666666
Comme Alexandr a si cette réponse reste IEEE 754 est un
seeeeeeeefffffffffffffffffffffff
double est
seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
avec 52 bits de fraction plutôt que le 23 qui unique a.
00111111001100110011... single
001111111110011001100110... double
0 01111110 01100110011... single
0 01111111110 01100110011... double
Tout comme 1/3 dans la base 10 est 0,3333333 ... pour toujours. Nous avons un motif répétitif ici 0110
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
Et voici la réponse.
if(x==0.7) return(1);
x contient 01100110011001100110011 que sa fraction, lorsque cela est converti en arrière doubler la fraction est
01100110011001100110011000000000....
qui ne soit pas égal à
01100110011001100110011001100110...
ici
if(x==0.7f) return(1);
qui ne marche pas de promotion se produire les mêmes modèles binaires sont comparés entre eux.
Pourquoi 1,0 travail?
00000048 <fun12>:
48: e3a005fe mov r0, #1065353216 ; 0x3f800000
4c: e12fff1e bx lr
00000050 <fun13>:
50: e3a00000 mov r0, #0
54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc>
58: e12fff1e bx lr
5c: 3ff00000 svccc 0x00f00000 ; IMB
0011111110000000...
0011111111110000000...
0 01111111 0000000...
0 01111111111 0000000...
Dans les deux cas, la fraction est des zéros. Ainsi, la conversion de double en simple au double, il n'y a aucune perte de précision. Il convertit du simple au double exactement et la comparaison binaire des deux valeurs fonctionne.
Le plus haut voté et vérifié par réponse Halfdan est la bonne réponse, c'est un cas de précision mixte et vous ne devriez jamais faire une comparaison égale.
Le pourquoi était pas montré dans cette réponse. 0,7 échoue 1.0 fonctionne. Pourquoi 0,7 échec était pas représenté. Une double question 1.1 échoue aussi.
EDIT
Les égaux peuvent être retirés du problème ici, il est une autre question qui a déjà été répondu, mais il est le même problème et a également la « ce que le ... » choc initial.
int fun1 ( void )
{
float x=0.7;
if(x<0.7) return(1);
else return(0);
}
int fun2 ( void )
{
float x=0.6;
if(x<0.6) return(1);
else return(0);
}
Disassembly of section .text:
00000000 <fun1>:
0: e3a00001 mov r0, #1
4: e12fff1e bx lr
00000008 <fun2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
Pourquoi un spectacle comme moins et l'autre au moins? Quand ils doivent être égaux.
De là-haut, nous connaissons l'histoire 0,7.
01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.
01100110011001100110011000000000....
est inférieur.
01100110011001100110011001100110...
0,6 est un motif répétitif différent 0011 au lieu de 0110.
mais lorsqu'il est converti à partir d'un double à un seul ou en général lorsqu'il est représenté comme une seule IEEE 754.
00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single
IEEE 754 utilise les modes d'arrondi, arrondir, arrondir ou rond à zéro. Compilateurs ont tendance à arrondir par défaut. Si vous vous souvenez dans l'arrondissement école primaire 12345678 si je voulais arrondir au 3ème chiffre du haut, il serait 12300000, mais tour à l'autre chiffre 1.235.000 si le chiffre après est de 5 ou plus arrondir ensuite. 5 est 1/2 de la base 10 (décimal) en binaire 1 est 1/2 de la base, donc si le chiffre après la position que nous voulons arrondir est 1 tour ensuite d'autre Do not. Donc, pour nous ne avons pas 0,7 arrondissons, pour 0,6 nous n'arrondissons.
Et il est maintenant facile de voir que
00110011001100110011010
converti en une double raison de (x <0,7)
00110011001100110011010000000000....
est supérieur à
00110011001100110011001100110011....
Alors, sans avoir à parler à l'aide est égale à la question se présente encore 0,7 est le double 0.7f est simple, l'opération est promu la plus haute précision si elles diffèrent.
Considérez ceci:
int main()
{
float a = 0.7;
if(0.7 > a)
printf("Hi\n");
else
printf("Hello\n");
return 0;
}
si (0,7> a) ici a est une variable flottante et 0.7
est une double constante. La double 0.7
constante est supérieure à la variable flottante a. D'où la condition est satisfaite si et il imprime 'Hi'
Exemple:
int main()
{
float a=0.7;
printf("%.10f %.10f\n",0.7, a);
return 0;
}
Sortie: 0,7000000000 0,6999999881
Si vous changez de type de données f deux , il va imprimer égale , Ceci est parce que les constantes de virgule flottante stockées dans deux et non flottant dans longue , double précision est élevée et le flotteur a moins de valeur précise, double stocké dans 64 bit valeur binaire et le flotteur stocké dans 32 binaire, il sera tout à fait clair si vous voyez la méthode des nombres à virgule flottante conversion à la conversion binaire.