Sé que usar ==
para verificar la igualdad de las variables de punto flotante no es una buena manera. Pero solo quiero saber eso con las siguientes declaraciones:
float x = ...
float y = x;
assert(y == x)
Como y
se copia de x
, ¿será cierta la afirmación?
c++
floating-point
Wei Li
fuente
fuente
-m32
) o instruyendo a GCC para que use la FPU x87 (-mfpmath=387
).Respuestas:
Además del
assert(NaN==NaN);
caso señalado por kmdreko, puede tener situaciones con x87-math, cuando los flotantes de 80 bits se almacenan temporalmente en la memoria y luego se comparan con los valores que aún se almacenan dentro de un registro.Posible ejemplo mínimo, que falla con gcc9.2 cuando se compila con
-O2 -m32
:Demostración de Godbolt: https://godbolt.org/z/X-Xt4R
El
volatile
probablemente se puede omitir, si logra crear suficientes registro a la presión de habery
almacenado y vuelve a cargar desde la memoria (pero lo suficientemente confundir al compilador, no omitir la comparación todos juntos).Consulte la referencia de preguntas frecuentes de GCC:
fuente
float
precisión estándar con una precisión adicional.-ffloat-store
parece ser la forma de prevenir esto.No será cierto si
x
es asíNaN
, ya que las comparacionesNaN
son siempre falsas (sí, inclusoNaN == NaN
). Para todos los demás casos (valores normales, valores subnormales, infinitos, ceros) esta afirmación será verdadera.El consejo para evitar los
==
flotantes se aplica a los cálculos debido a que los números de coma flotante no pueden expresar muchos resultados exactamente cuando se usan en expresiones aritméticas. La asignación no es un cálculo y no hay razón para que la asignación arroje un valor diferente al original.La evaluación de precisión extendida no debería ser un problema si se sigue el estándar. De
<cfloat>
heredado de C [5.2.4.2.2.8] ( énfasis mío ):Sin embargo, como los comentarios han señalado, algunos casos con ciertos compiladores, opciones de compilación y objetivos podrían hacer que esto sea paradójicamente falso.
fuente
x
se calcula en un registro en la primera línea, manteniendo más precisión que el mínimo para afloat
. Ely = x
puede estar en la memoria, manteniendo solo lafloat
precisión. Luego, la prueba de igualdad se haría con la memoria contra el registro, con diferentes precisiones y, por lo tanto, sin garantía.x+pow(b,2)==x+pow(a,3)
podría diferir deauto one=x+pow(b,2); auto two=y+pow(a,3); one==two
porque uno podría comparar usando más precisión que el otro (si uno / dos son valores de 64 bits en ram, mientras que los valores intermedistas son 80 bits en fpu). Entonces la asignación puede hacer algo, a veces.gcc -ffloat-store
para un cumplimiento estricto. Pero esta pregunta se tratax=y; x==y;
sin hacer nada a ninguna de las dos. Siy
ya está redondeado para caber en un flotador, la conversión a doble o largo doble y viceversa no cambiará el valor. ...Sí,
y
seguramente asumirá el valor dex
:No hay margen de maniobra para asignar otros valores.
(Otros ya han señalado que una comparación de equivalencia
==
evaluará losfalse
valores de NaN).El problema habitual con el punto flotante
==
es que es fácil no tener el valor que crees que tienes. Aquí, sabemos que los dos valores, sean cuales sean, son los mismos.fuente
[expr]
. Si voy a ignorar los enlaces y centrarme en las citas, me queda la confusión de que, por ejemplo, C.5.3 no parece abordar el uso del término "valor" o el término "resultado" (aunque sí use "resultado" una vez en su contexto normal en inglés). Quizás podría describir más claramente dónde cree que el estándar hace una distinción y proporcionar una sola cita clara para que esto suceda. ¡Gracias!Sí, en todos los casos (sin tener en cuenta los problemas de NaN y x87), esto será cierto.
Si haces una
memcmp
prueba en ellos, podrás probar la igualdad mientras comparas NaNs y sNaNs. Esto también requerirá que el compilador tome la dirección de la variable que coaccionará el valor a 32 bits enfloat
lugar de 80 bits. Esto eliminará los problemas x87. La segunda afirmación aquí pretende no mostrar que==
no comparará los NaN como verdaderos:Tenga en cuenta que si los NaN tienen una representación interna diferente (es decir, una mantisa diferente), la
memcmp
comparación no será verdadera.fuente
En los casos habituales, se evaluaría como verdadero. (o la declaración de afirmación no hará nada)
Editar :
Por "casos habituales" me refiero a que estoy excluyendo los escenarios antes mencionados (como los valores de NaN y las unidades de punto flotante de 80x87) según lo señalado por otros usuarios.
Dada la obsolescencia de los chips 8087 en el contexto actual, el problema está bastante aislado y para que la pregunta sea aplicable en el estado actual de la arquitectura de punto flotante utilizada, es cierto para todos los casos, excepto para NaNs.
(referencia sobre 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )
Felicitaciones a @chtz por reproducir un buen ejemplo y a @kmdreko por mencionar NaNs: ¡no los conocíamos antes!
fuente
x
estar en un registro de coma flotante mientrasy
se carga desde la memoria. La memoria puede tener menos precisión que un registro, lo que hace que falle la comparación.float
valor sin precisión adicional.int a=1; int b=a; assert( a==b );
una afirmación, creo que solo tiene sentido responder esta pregunta en relación con un compilador que funcione correctamente (aunque posiblemente tenga en cuenta que algunas versiones de algunos compiladores sí / no tienen -se sabe-para hacer esto mal). En términos prácticos, si por alguna razón un compilador no elimina la precisión adicional del resultado de una asignación almacenada en el registro, debe hacerlo antes de usar ese valor.Sí, devolverá True siempre, excepto si es NaN . Si el valor de la variable es NaN , ¡siempre devuelve False !
fuente