2011-07-17 9 views
15

Duplicar posible:
strange output in comparision of float with float literal¿Por qué comparar conductores dobles y flotantes para obtener resultados inesperados?

float f = 1.1; 
double d = 1.1; 
if(f == d) // returns false! 

¿Por qué es así?

+0

http://www.boost.org/doc/libs/1_34_1/libs/test/doc/components/test_tools/floating_point_comparison.html – darlinton

+11

Google "lo que todo científico de la computación debe saber sobre la aritmética de punto flotante" para algunas interesantes lecturas . – Bart

+2

Nunca use '==' para comparar valores de coma flotante. En su lugar, use algo más como 'if (abs (f - d) <0.001)'. – aroth

Respuesta

30

Los factores importantes que se examinan con float o double números son:
precisión & Completando


Precisión:
La precisión de un número de coma flotante es cuántos dígitos puede representar sin perder ninguna información que contenga.

Considere la fracción 1/3. La representación decimal de este número es 0.33333333333333… con 3 saliendo al infinito. Un número infinito de longitud requeriría que se representara la memoria infinita con precisión exacta, pero los tipos de datos float o double normalmente solo tienen 4 o 8 bytes. Por lo tanto, los números dobles del punto flotante & solo pueden almacenar una cierta cantidad de dígitos y el resto se perderá. Por lo tanto, no existe una forma definida y precisa de representar números flotantes o dobles con números que requieren más precisión de la que pueden contener las variables.


Redondeo:
hay un no evidentes diferencias entre binary y decimal (base 10) números.
Considere la fracción 1/10. En decimal, esto se puede representar fácilmente como 0.1, y 0.1 se puede considerar como un número fácil de representar. Sin embargo, en binario, 0.1 está representado por la secuencia infinita: 0.00011001100110011…

Un ejemplo:

#include <iomanip> 
int main() 
{ 
    using namespace std; 
    cout << setprecision(17); 
    double dValue = 0.1; 
    cout << dValue << endl; 
} 

Esta salida es:

0.10000000000000001 

Y no

0.1. 

Este es porque el doble tuvo que truncar la aproximación debido a su memoria limitada y, que da como resultado un número que no es exactamente 0.1. Tal escenario se llama Error de redondeo.


Cuando se comparan dos cerca de flotación y números dobles tales errores de redondeo en funcionamiento y, finalmente, la comparación arroja resultados incorrectos y esta es la razón por la que nunca se debe comparar los números de punto flotante o doble, con ==.

Lo mejor que puedes hacer es tomar su diferencia y comprobar si es menos de un epsilon.

abs(x - y) < epsilon 
+0

Esta es una respuesta válida, +1 para compensar votos atrasados. Deja información que desear, pero es válida de todos modos. –

+1

Se pueden representar con bastante precisión, pero no exactamente en todos los casos. – aroth

+1

@Als: esta información es mucho mejor. Convertí mi voto negativo a voto positivo y eliminé mis comentarios en desacuerdo :) –

2

El IEEE 754 de 32 bits puede almacenar float: 1.1000000238...
El IEEE 754 de 64 bits puede almacenar double: 1.1000000000000000888...

Vea por qué no son "iguales"?


En IEEE 754, fracciones se almacenan en potencias de 2:

2^(-1), 2^(-2), 2^(-3), ... 
1/2, 1/4, 1/8, ... 

Ahora necesitamos una manera de representar 0.1. Esta es (una versión simplificada de) la de 32 bits IEEE 754 de representación (float):

2^(-4) + 2^(-5) + 2^(-8) + 2^(-9) + 2^(-12) + 2^(-13) + ... + 2^(-24) + 2^(-25) + 2^(-27) 
00011001100110011001101 
1.10000002384185791015625 

Con 64 bits double, es aún más precisa. No se detiene en 2^(-25), sigue funcionando aproximadamente el doble. (2^(-48) + 2^(-49) + 2^(-51), tal vez?)


Recursos

IEEE 754 Converter (32-bit)

0

flotadores y los dobles se almacenan en un formato binario que no puede representar todos los números exactamente (es imposible representar infinitamente muchos posibles números diferentes en un espacio finito).

Como resultado redondean. El flotador tiene que redondear más del doble, porque es más pequeño, por lo que 1.1 redondeado al Float válido más cercano es diferente a 1.1 redondeado al doble de valud más cercano.

para ver qué números son válidos y los flotadores dobles ver Floating Point

4

En general, usted no debe comparar los flotadores a los flotadores, dobles a dobles, o flotadores a duplica utilizando ==.

La mejor práctica es restarlas y comprobar si el valor absoluto de la diferencia es menor que un pequeño epsilon.

if(std::fabs(f - d) < std::numeric_limits<float>::epsilon()) 
{ 
    // ... 
} 

Una razón es porque los números de coma flotante son (más o menos) fracciones binarias, y sólo puede aproximarse a muchos números decimales. Muchos números decimales deben convertirse necesariamente en "decimales" binarios repetitivos o números irracionales. Esto introducirá un error de redondeo.

From wikipedia:

Por ejemplo, 1/5 no se puede representar exactamente como un número de coma flotante utilizando una base binaria, pero se puede representar exactamente utilizando una base decimal.

En su caso particular, un flotador y doble tendrá diferente redondeo para la fracción irracional/repitiendo que debe ser utilizado para representar 1.1 en binario. Le será difícil conseguir que sean "iguales" después de que sus conversiones correspondientes hayan introducido diferentes niveles de error de redondeo.

El código que proporcioné anteriormente resuelve esto simplemente verificando si los valores están dentro de un delta muy corto. Su comparación cambia de "¿estos valores son iguales?" "¿Son estos valores dentro de un pequeño margen de error el uno del otro?"

Además, ver a esta pregunta: What is the most effective way for float and double comparison?

También hay una gran cantidad de otras rarezas unos números de punto que rompen un simple comparación de igualdad flotante. Comprobar este artículo para obtener una descripción de algunos de ellos:

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

+0

El vecindario ε generalmente no es la mejor práctica, sino más bien una solución perezosa. La mayoría de las veces, realmente no quieres '==' de todos modos, sino '<=' or '> =', y solo piensas que necesitas '==' porque eso funciona bien para los enteros ya que el caso '>' nunca se alcanza con estos. - También, a veces tú, de hecho, deseas '' == 'para flotantes, aunque esto ocurre bastante rara vez. – leftaroundabout

+0

@leftaroundabout: Esto es contrario a la mayoría de los artículos que he leído. ¿Puedes dar más detalles sobre estos casos? Tal vez agregue su propia respuesta :) –

7

intente ejecutar este código, los resultados harán que la razón obvia.

#include <iomanip> 
#include <iostream> 

int main() 
{ 
    std::cout << std::setprecision(100) << (double)1.1 << std::endl; 
    std::cout << std::setprecision(100) << (float)1.1 << std::endl; 
    std::cout << std::setprecision(100) << (double)((float)1.1) << std::endl; 
} 

La salida:

1.100000000000000088817841970012523233890533447265625 
1.10000002384185791015625 
1.10000002384185791015625 

Ni float ni double puede representar 1,1 con precisión. Cuando intenta hacer la comparación, el número flotante se convierte de forma implícita en un doble. El tipo de datos dobles puede representar con precisión el contenido del flotante, por lo que la comparación arroja resultados falsos.

Cuestiones relacionadas