Pregunta

Yo tenía un pequeño momento WTF esta mañana. THS WTF se puede resumir con esto:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

La razón parece ser que la expresión x + y es ascendido a doblar y se compara con la versión truncada en z. (Si cambio z a double la aserción no se activa).

Puedo ver que por razones de precisión que tendría sentido para llevar a cabo todas las aritméticas en coma flotante de doble precisión antes de convertir el resultado a la precisión simple. He encontrado el siguiente párrafo en la norma (que supongo que tipo de ya sabía, pero no en este contexto):

4.6.1. "Un valor p de tipo float se puede convertir en un valor p de tipo double. El valor es sin cambios"

Mi pregunta es, se x + y garantizado para ser promovido a duplicar o es a discreción del compilador?

ACTUALIZACIÓN: Dado que muchas personas han afirmado que no se debería utilizar == de punto flotante, sólo quería decir que en el caso concreto que estoy trabajando, una comparación exacta está justificada.

punto flotante comparación es complicado, he aquí un interesante sobre el tema que creo que no se ha mencionado.

¿Fue útil?

Solución

No se puede asumir que generalmente == funcionará como se espera para este tipo de punto flotante. Comparar valores redondeados o utilizar construcciones como abs(a-b) < tolerance su lugar.

La promoción es enteramente a discreción del compilador (y dependerá de hardware de destino, nivel de optimización, etc.).

Lo que está pasando en este caso particular, es casi seguro que los valores se almacenan en la FPU se registra en una precisión mayor que en la memoria - en general, hardware FPU moderna trabaja con doble o mayor precisión internamente lo precisión el programador pidió, con el código de generación de compilador para hacer las conversiones apropiadas cuando los valores se almacenan en la memoria; en una acumulación unoptimised, el resultado de x+y se encuentra todavía en un registro en el punto se hace la comparación, pero z habrá sido almacenado a la memoria y fue a buscar la espalda, y por lo tanto truncado flotar precisión.

Otros consejos

El Documento de trabajo para el próximo estándar C ++ 0x la sección 5 del punto 11 dice

  

Los valores de los operandos flotantes y los resultados de las expresiones flotantes pueden ser representados en una mayor precisión y rango que el requerido por el tipo; los tipos no se cambian con ello

Así que a criterio del compilador.

El uso de gcc 4.3.2, la afirmación es no se activa, y, de hecho, el valor de lado derecho de regresar de x + y es un float, en lugar de un double.

Así que es hasta el compilador. Por esta razón, nunca es prudente confiar en la igualdad exacta entre dos valores de coma flotante.

El C ++ FAQ Lite tiene algunos debates sobre el tema:

Es el problema, ya que el número de flotación a la conversión binaria no da una precisión exacta.

Y dentro sizeof(float) bytes que no puede acomodar valor preciso del número de flotador y operación aritmética puede conducir a la aproximación y por lo tanto no la igualdad.

Ver abajo por ejemplo.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert

Me parece que sería a discreción del compilador, pero siempre se podría obligar a que con un reparto si esa era su forma de pensar?

Sin embargo, otra razón para no comparar directamente los flotadores.

if (fabs(result - expectedResult) < 0.00001)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top