2011-11-09 17 views
7

Me encontré con el siguiente ejemplo en wikipedia (http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion).Conversión de tipo implícito en C

#include <stdio.h> 

int main() 
{ 
    int i_value = 16777217; 
    float f_value = 16777217.0; 
    printf("The integer is: %i\n", i_value); // 16777217 
    printf("The float is: %f\n", f_value); // 16777216.000000 
    printf("Their equality: %i\n", i_value == f_value); // result is 0 
} 

Su explicación: "Este extraño comportamiento es causado por una conversión implícita de i_value flotar cuando se compara con f_value; un elenco que pierde precisión, por lo que los valores que se comparan diferentes"

¿No es esto incorrecto? Si se arrojó i_value para flotar, ambos tendrían la misma pérdida de precisión y serían iguales. Por lo tanto, i_value debe convertirse al doble.

+0

Con g ++ (GCC 4.6.2) obtengo '1' para igualdad. –

+0

@Kerrek: Y a mí. En VS, obtengo 0. –

+0

@OliCharlesworth: tengo curiosidad por cambiar el literal a 'f' o el tipo a' doble' - obtengo '1' en todos los casos ... –

Respuesta

7

No, en el caso del operador de igualdad, las "conversiones aritméticas habituales" ocurren, que comienzan:

  • En primer lugar, si el tipo de bienes correspondiente de alguno de los operandos es long double, la otro operando se convierte, sin cambio de tipo de dominio, a un tipo cuyo tipo real correspondiente es long double.
  • De lo contrario, si el tipo real correspondiente de cualquiera de los operandos es double, el otro operando se convierte, sin cambio de tipo de dominio, a un tipo cuyo tipo real correspondiente es double.
  • De lo contrario, si el tipo real correspondiente de cualquiera de los operandos es float, el otro operando se convierte, sin cambio de tipo de dominio, a un tipo cuyo tipo real correspondiente es float.

Este último caso se aplica aquí: i_value se convierte en float.

La razón por la que se puede ver un extraño resultado de la comparación, pesar esto, es a causa de esta advertencia a las conversiones aritméticas habituales:

Los valores de operandos flotantes y de los resultados de la flotación Las expresiones se pueden representar con mayor precisión y rango que que requiere el tipo; los tipos no son cambiados por eso.

Esto es lo que está sucediendo: el tipo de la i_value convertido sigue siendo float, pero en esta expresión el compilador está tomando ventaja de esta latitud y representándolo en mayor precisión que float. Este es el comportamiento típico del compilador cuando se compila para un punto flotante compatible con 387, porque el compilador deja valores temporales en la pila de coma flotante, que almacena números de coma flotante en un formato de precisión extendida de 80 bits.

Si su compilador es gcc, puede desactivar esta precisión adicional dando la opción de línea de comando -ffloat-store.

+0

En x64 gcc usa instrucción cvtsi2ssl explícita que convierte el número entero en flotación. En el x86, sin embargo, esto es exactamente lo que sucede y esa mayor precisión es de hecho incluso mayor que el doble. –

+0

@ konrad.kruczynski: Sí, y puede obtener el mismo resultado en x86 suministrando la opción '-mfpmath = sse' (que también requiere' -msse' o una opción que implique eso). – caf

-1

Creo que el valor entero más grande que puede contener un punto flotante IEEE de 32 bits es 1048576, que es más pequeño que el número anterior. Entonces, definitivamente es cierto que el valor del punto flotante no será exactamente 16777217.

La parte de la que no estoy seguro es cómo hace el compilador la comparación entre dos tipos diferentes de números (es decir, un flotante y un int) . Se me ocurren tres maneras diferentes se puede hacer esto:

1) convierten ambos valores a "flotar" (esto debería hacer que los valores de la misma, por lo que este no es probablemente lo que hace el compilador)

2) Convierta ambos valores en "int" (esto puede o no mostrarles lo mismo ... la conversión a un int a menudo trunca, por lo que si el valor del punto flotante es 16777216.99999, entonces la conversión a "int" truncaría)

3) Convierta ambos valores a "doble". Creo que esto es lo que haría el compilador. Si esto es lo que hace el compilador, los dos valores definitivamente serían diferentes. Un doble puede contener 16777217 exactamente, y también podría representar exactamente el valor del punto flotante al que se convierte 16777217.0 (que no es exactamente 16777217.0).

+3

1048576 es 2^20, entonces esto es incorrecto. –

0

Aquí hay algunas buenas respuestas. Debe tener mucho cuidado al convertir entre varios enteros y varias representaciones de coma flotante.

Por lo general, no pruebo los números de coma flotante para la igualdad, especialmente si uno de ellos proviene de una conversión implícita o explícita de un tipo entero. Trabajo en una aplicación que está llena de cálculos geométricos. En la medida de lo posible, trabajamos con enteros normalizados (forzando una precisión máxima que aceptaremos en los datos de entrada). Para los casos en los que debe usar el punto flotante, aplicaremos un valor absoluto a la diferencia si se necesita una comparación.

Cuestiones relacionadas