2012-05-23 13 views
8

En el siguiente código, las funciones foo1, foo2 y foo3 son equivalentes. Sin embargo, cuando ejecutar foo3 no termina desde el ciclo, ¿hay alguna razón por la cual este sea el caso?Cálculos en coma flotante IEEE-754, igualdad y estrechamiento

template <typename T> 
T foo1() 
{ 
    T x = T(1); 
    T y = T(0); 
    for (;;) 
    { 
     if (x == y) break; 
     y = x; 
     ++x; 
    } 
    return x; 
} 

template <typename T> 
T foo2() 
{ 
    T x = T(0); 
    for (;;) 
    { 
     T y = x + T(1); 
     if (!(x != y)) break; 
     ++x; 
    } 
    return x; 
} 

template <typename T> 
T foo3() 
{ 
    T x = T(0); 
    while (x != (x + T(1))) ++x; 
    return x; 
} 

int main() 
{ 
    printf("1 float: %20.5f\n", foo1<float>()); 
    printf("2 float: %20.5f\n", foo2<float>()); 
    printf("3 float: %20.5f\n", foo3<float>()); 
    return 0; 
} 

Nota: Este fue compilado usando VS2010 con/fp precisa en modo de lanzamiento. No estoy seguro de cómo GCC, etc. trataría este código, cualquier información sería genial. ¿Podría ser esto un problema donde en foo3, los valores x y x + 1 se convierten en NaN de alguna manera?

+3

Problema de interés. Las tres funciones terminan como se esperaba en gcc 4.2.1. Estoy tentado de llamarlo un error en VS. – ComicSansMS

+3

Hmm. Huele como una optimización exagerada (es decir, un error de compilación) para mí. –

+2

@MarkDickinson: se bloquea para mí en una compilación de depuración en VS2010 –

Respuesta

13

Lo que sucede es muy probablemente lo siguiente. En el arco x86, los cálculos intermedios se pueden realizar con 80 bits de precisión (el doble largo es el tipo correspondiente de C/C++). El compilador usa todos los 80 bits para la operación (+1) y para la operación (! =), Pero trunca los resultados antes del almacenamiento.

Así que lo que su compilador que realmente hace es la siguiente:

while ((long double)(x) != ((long double)(x) + (long double)(1))) { 
    x = (float)((long double)(x) + (long double)(1)); 
} 

Esto es absolutamente no-IEEE-conformes y provoca un sinfín de dolores de cabeza para todos, pero este es el valor predeterminado para MSVC. Use el indicador del compilador /fp:strict para deshabilitar este comportamiento.

Este es mi recuerdo del problema de hace unos 10 años, así que por favor, perdónenme si esto de alguna manera no es del todo correcto. See this for the official Microsoft documentation.

EDITAR I era muy sorprenda al saber que g ++ de exposiciones predeterminados exactamente el mismo comportamiento (en Linux i386, pero no con por ejemplo -mfpmath = sse).

+4

+1. Solo usar 'double' en lugar de' long double' sería suficiente para causar problemas aquí, pero a la MS de IIRC todavía le gusta usar la FPU x87 incluso para compilaciones de 64 bits, por lo que 'long double' parece más probable. –

+1

¡Excelente respuesta! gracias. –

+1

Excelente respuesta bien informada, pero noto que en VS2008 incluso con '/ fp: strict' foo3() todavía no termina? – acraig5075

Cuestiones relacionadas