c casting floating-point visual-studio-6 gcc3

Comportamiento extraño al lanzar un int para flotar en C



casting floating-point (5)

Algunos de ustedes dijeron que es un error de optimización, pero estoy un poco en desacuerdo. Creo que es un error de precisión de punto flotante razonable y un buen ejemplo que muestra a las personas cómo funciona el punto flotante.

http://ideone.com/Ssw8GR

quizás OP podría intentar pegar mi programa en su computadora e intentar compilar con su compilador y ver qué sucede. o tratar:

http://ideone.com/OGypBC

(Con conversión explícita de flotación).

De todos modos, si calculamos el error, es 4.656612875245797e-10 , y debe considerarse bastante preciso.

También podría estar relacionado con la preferencia de printf .

Tengo una duda sobre la salida del siguiente programa en C. Intenté compilarlo utilizando tanto Visual C ++ 6.0 como MinGW32 (gcc 3.4.2).

#include <stdio.h> int main() { int x = 2147483647; printf("%f/n", (float)2147483647); printf("%f/n", (float)x); return 0; }

La salida es:

2147483648.000000 2147483647.000000

Mi pregunta es: ¿por qué son ambas líneas diferentes? Cuando convierte el valor entero 2147483647 al formato de punto flotante IEEE 754, se aproxima a 2147483648.0. Por lo tanto, esperaba que ambas líneas fueran iguales a 2147483648.000000.

EDITAR : El valor "2147483647.000000" no puede ser un valor de punto flotante de precisión simple, ya que el número 2147483647 no se puede representar exactamente en el formato de punto flotante de precisión simple IEEE 754 sin pérdida de precisión.


En ambos casos, el código busca convertir de algún tipo de entero a float y luego a double . La double conversión se produce porque es un valor float que se pasa a una función variable.

Verifique su configuración de FLT_EVAL_METHOD , sospeche que tiene un valor de 1 o 2 (OP informa 2 con al menos un compilador). Esto permite al compilador evaluar las operaciones y constantes de float "al rango y la precisión" mayores que float .

Su compilador optimizado (float)x va directamente a double aritmética. Esta es una mejora de rendimiento durante el tiempo de ejecución.

(float)2147483647 es un tiempo de compilación y el compilador está optimizado para que int float a double precisión, ya que el rendimiento no es un problema aquí.

[Edit2] Es interesante que la especificación C11 es más específica que la especificación C99 con la adición de "Excepto por asignación y conversión ...". Esto implica que los compiladores C99 a veces permitían que int double la conversión directa, sin pasar primero por float y que se modificó C11 para que claramente no permitiera omitir un lanzamiento.

Con C11 excluyendo formalmente este comportamiento, los cumplidores modernos no deberían hacer esto, pero los más antiguos, como el poder de los OP, son un error de los estándares C11. A menos que se encuentre otra especificación de C99 o C89 para decir lo contrario, esto parece ser un comportamiento permisible del compilador.

[Editar] Tomando los comentarios juntos de @Keith Thompson, @tmyklebu, @Matt McNabb, se debe esperar que el compilador, incluso con un FLT_EVAL_METHOD distinto de cero, produzca 2147483648.0... Por lo tanto, o bien un indicador de optimización del compilador está sobrepasando explícitamente el comportamiento correcto o el compilador tiene un error de esquina.

C99dr §5.2.4.2.2 8 Los valores de las operaciones con operandos flotantes y los valores sujetos a las conversiones aritméticas habituales y las constantes flotantes se evalúan a un formato cuyo rango y precisión pueden ser mayores que los requeridos por el tipo. El uso de formatos de evaluación se caracteriza por el valor definido por la implementación de FLT_EVAL_METHOD :

-1 indeterminable;

0 evalúa todas las operaciones y constantes solo al rango y precisión del tipo;

1 evalúo operaciones y constantes de tipo float y double al rango y precisión del tipo double , evalúo operaciones long double y constantes al rango y precisión del tipo long double largo;

2 evalúe todas las operaciones y constantes para el rango y la precisión del tipo long double .

C11dr §5.2.4.2.2 9 Excepto por asignación y conversión (que eliminan todo rango y precisión adicionales), los valores producidos por operadores con operandos flotantes y valores sujetos a las conversiones aritméticas habituales y de constantes flotantes se evalúan en un formato cuyo rango y la precisión puede ser mayor que la requerida por el tipo. El uso de formatos de evaluación se caracteriza por el valor definido por la implementación de FLT_EVAL_METHOD

-1 (igual que C99)

0 (igual que C99)

1 (igual que C99)

2 (igual que C99)


En el primer printf , el compilador realiza la conversión de entero a flotador. En el segundo, lo hace la biblioteca C runtime. No hay ninguna razón particular por la que deban producir respuestas idénticas en los límites de su precisión.


Este es sin duda un error de compilación. Desde el estándar C11 tenemos las siguientes garantías (C99 era similar):

  • Los tipos tienen un conjunto de valores representables (implícitos)
  • Todos los valores representables por float también se pueden representar por double (6.2.5 / 10)
  • Convertir float a double no cambia el valor (6.3.1.5/1)
  • La int de int a float , cuando el valor int se encuentra en el conjunto de valores representables para float , proporciona ese valor.
  • La int de int a float , cuando la magnitud del valor de int es menor que FLT_MAX y int no es un valor representable para float , hace que se seleccione el siguiente valor de float más alto o más bajo, y cuál de ellos se seleccione para su implementación -definido (6.3.1.4/2)

El tercero de estos puntos garantiza que las promociones de argumentos predeterminados no modifiquen el valor float suministrado a printf .

Si 2147483647 se puede representar en float , entonces (float)x (float)2147483647 debe dar 2147483647.000000 .

Si 2147483647 no se puede representar en float , entonces (float)x y (float)2147483647 deben dar el siguiente float más alto o el siguiente más bajo. Ambos no tienen que hacer la misma selección. Pero esto significa que una copia impresa de 2147483647.000000 no está permitida 1 , cada una debe ser el valor más alto o el más bajo.

1 Bueno, es teóricamente posible que el siguiente flotador más bajo fuera 2147483646.9999999... así que cuando el valor se muestra con una precisión de 6 dígitos por printf , se redondea para dar lo que se vio. Pero esto no es cierto en IEEE754 y puede experimentar fácilmente para descartar esta posibilidad.


Visual C ++ 6.0 fue lanzado el siglo pasado, y creo que es anterior al C ++ estándar. No es sorprendente que VC ++ 6.0 muestre un comportamiento roto.

También notará que gcc-3.4.2 es de 2004. De hecho, está utilizando un compilador de 32 bits. gcc en x86 juega bastante rápido y suelto con matemáticas de punto flotante . Esto puede técnicamente estar justificado por el estándar C si gcc establece FLT_EVAL_METHOD en algo distinto de cero.