tipos tipo programacion float entre diferencia datos c++ visual-c++

c++ - tipo - ¿Por qué comparar el doble y el flotante lleva a resultados inesperados?



tipo float (5)

Los factores importantes que se consideran con float o números double son:
Precisión y Redondeo

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

Considera la fracción 1/3 . La representación decimal de este número es 0.33333333333333… con 3 saliendo al infinito. Un número de longitud infinito requeriría que la memoria infinita se represente con precisión exacta, pero los tipos de datos float o double generalmente solo tienen 4 u 8 bytes. Por lo tanto, el número flotante y los números dobles 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 diferencias no obvias entre binary números binary y decimal (base 10) .
Considera la fracción 1/10 . En decimal , esto se puede representar fácilmente como 0.1 , y 0.1 puede considerarse como un número fácilmente representable. 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.

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

Cada vez que se comparan dos números flotantes cercanos y dobles, tales errores de redondeo entran en juego y, finalmente, la comparación arroja resultados incorrectos y esta es la razón por la que nunca debe comparar los números de punto flotante o el doble usando == .

Lo mejor que puede hacer es tomar su diferencia y comprobar si es menor que un épsilon.

abs(x - y) < epsilon

Posible duplicado:
salida extraña en comparación de flotación con literal flotante

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

¿Por que es esto entonces?


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

¿Ves por qué no son "iguales"?

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

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

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

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

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

Recursos

Convertidor IEEE 754 (32 bits)


Intenta ejecutar este código, los resultados harán que la razón sea 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 el float ni el double pueden 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.


Los flotantes y los dobles se almacenan en un formato binario que no puede representar todos los números exactamente (es imposible representar infinitamente muchos números diferentes posibles 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 flotantes y dobles válidos, vea Punto flotante


En general, no debes comparar las carrozas con las carrozas, las dobles con las dobles o las carrozas con las dobles con == .

La mejor práctica es restarlos y verificar 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 punto flotante son (más o menos) fracciones binarias, y solo pueden 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.

De la wikipedia :

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

En su caso particular, un float y un double tendrán un redondeo diferente para la fracción irracional / repetitiva que debe usarse 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 di más arriba 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, vea esta pregunta: ¿Cuál es la forma más efectiva para la comparación de flotación y doble?

También hay muchas otras rarezas acerca de los números de coma flotante que rompen una simple comparación de igualdad. Consulte este artículo para obtener una descripción de algunos de ellos:

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