9

Así que tienen una función que se ve algo como esto:¿Puede modificar las variables de alguna manera?

float function(){ 
    float x = SomeValue; 
    return x/SomeOtherValue; 
} 

En algún momento, esta función se desborda y devuelve un valor negativo muy grande. Para tratar de localizar a exactamente donde esto ocurría, añadí una declaración cout para que la función era la siguiente:

float function(){ 
    float x = SomeValue; 
    cout << x; 
    return x/SomeOtherValue; 
} 

y funcionó! Por supuesto, resolví el problema completamente usando un doble. Pero tengo curiosidad de por qué la función funcionó correctamente cuando lo presenté. ¿Es esto típico, o podría haber un error en otro lugar que me estoy perdiendo?

(Si te sirve de ayuda, el valor almacenado en el flotador es sólo un valor entero, y no es particularmente grande. Yo sólo lo puso en un flotador para evitar la fundición.)

Respuesta

18

Bienvenido al maravilloso mundo de la coma flotante. La respuesta que obtenga probablemente dependerá del modelo de punto flotante con el que compiló el código.

Esto sucede debido a la diferencia entre la especificación IEEE y el hardware en el que se está ejecutando el código. Es probable que su CPU tenga registros de coma flotante de 80 bits que se utilizan para mantener el valor flotante de 32 bits. Esto significa que hay mucha más precisión mientras el valor permanece en un registro que cuando se lo fuerza a una dirección de memoria (también conocido como 'origen' del registro).

Cuando pasó el valor de cout el compilador tuvo que escribir el punto flotante en la memoria, y esto da como resultado una pérdida de precisión y un comportamiento interesante de los casos de desbordamiento de WRT.

Consulte la documentación de MSDN en VC++ floating point switches. Puedes intentar compilar con/fp: strict y ver qué pasa.

+0

También hay una nota de GCC para esto en http://gcc.gnu.org/wiki/x87note Debido a este maravilloso comportamiento, la comparación de los cálculos de coma flotante también se rompe inherentemente, a menos que se utilicen valores precalculados. – hazzen

3

Impresión de un valor a cout no debe cambiar el valor del parámetro de ninguna manera.

Sin embargo, he visto un comportamiento similar, al agregar declaraciones de depuración provoca un cambio en el valor. En esos casos, y probablemente también este, mi conjetura fue que las declaraciones adicionales estaban causando que el optimizador del compilador se comportara de manera diferente, por lo tanto, genere un código diferente para su función.

Agregar la declaración cout significa que el valor de x se usa directamente. Sin él, el optimizador podría eliminar la variable, cambiando así el orden del cálculo y, por lo tanto, cambiando la respuesta.

0

No creo que la acción tenga ningún efecto sobre la variable, el problema tendría que ser en otro lugar.

2

Como acotación al margen, siempre es una buena idea para declarar variables inmutables usando const:

float function(){ 
    const float x = SomeValue; 
    cout << x; 
    return x/SomeOtherValue; 
} 

Entre otras cosas, esto le impide pasar sin querer sus variables a las funciones que pueden modificarlos a través de const referencias no .

1

cout provoca una referencia a la variable, lo que a menudo hará que el compilador fuerce su derrame en la pila.

Debido a que es un flotante, esto probablemente causa que su valor se trunque de la doble representación doble o larga que normalmente tendría.

Llamar a cualquier función (no en línea) que tome un puntero o referencia a x debería terminar causando el mismo comportamiento, pero si el compilador más tarde se vuelve más inteligente y aprende a alinearlo, será igual de jodido :)

Cuestiones relacionadas