De acuerdo con esta página java.sun ==
es el operador de comparación de igualdad para números de coma flotante en Java.
Sin embargo, cuando escribo este código:
if(sectionID == currentSectionID)
en mi editor y ejecuto un análisis estático, obtengo: "JAVA0078 Valores de coma flotante en comparación con =="
¿Qué tiene de malo usar ==
para comparar valores de coma flotante? ¿Cuál es la forma correcta de hacerlo?
java
equality
floating-accuracy
usuario128807
fuente
fuente
Respuestas:
La forma correcta de probar flotadores para 'igualdad' es:
donde épsilon es un número muy pequeño como 0.00000001, dependiendo de la precisión deseada.
fuente
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
para abordar ese problema?Math.ulp()
en mi respuesta a esta pregunta.Los valores de coma flotante pueden desactivarse un poco, por lo que pueden no informar exactamente igual. Por ejemplo, estableciendo un flotante en "6.1" y luego imprimiéndolo nuevamente, puede obtener un valor reportado de algo como "6.099999904632568359375". Esto es fundamental para la forma en que funcionan los flotadores; por lo tanto, no desea compararlos utilizando la igualdad, sino más bien la comparación dentro de un rango, es decir, si la diferencia del flotante con el número con el que desea compararlo es menor que un cierto valor absoluto.
Este artículo en el Registro ofrece una buena descripción de por qué este es el caso; Lectura útil e interesante.
fuente
Solo para dar la razón detrás de lo que todos los demás dicen.
La representación binaria de un flotador es un poco molesta.
En binario, la mayoría de los programadores conocen la correlación entre 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d
Bueno, también funciona a la inversa.
.1b = .5d, .01b = .25d, .001b = .125, ...
El problema es que no hay una forma exacta de representar la mayoría de los números decimales como .1, .2, .3, etc. Todo lo que puede hacer es aproximarse en binario. El sistema hace un poco de redondeo cuando se imprimen los números para mostrar .1 en lugar de .10000000000001 o .999999999999 (que probablemente estén tan cerca de la representación almacenada como .1)
Editar del comentario: La razón por la que esto es un problema son nuestras expectativas. Esperamos totalmente que 2/3 se falsifique en algún momento cuando lo convertimos a decimal, ya sea .7 o .67 o .666667 .. Pero no esperamos automáticamente que .1 se redondee de la misma manera que 2/3 Y eso es exactamente lo que está sucediendo.
Por cierto, si tiene curiosidad, el número que almacena internamente es una representación binaria pura usando una "notación científica" binaria. Entonces, si le dice que almacene el número decimal 10.75d, almacenaría 1010b para el 10 y .11b para el decimal. Por lo tanto, almacenaría .101011 y luego guardaría algunos bits al final para decir: Mueva el punto decimal cuatro lugares a la derecha.
(Aunque técnicamente ya no es un punto decimal, ahora es un punto binario, pero esa terminología no habría hecho las cosas más comprensibles para la mayoría de las personas que encontrarían esta respuesta útil).
fuente
Porque no es cierto que
0.1 + 0.2 == 0.3
fuente
Float.compare(0.1f+0.2f, 0.3f) == 0
?Creo que hay mucha confusión sobre los flotadores (y los dobles), es bueno aclararlo.
No hay nada inherentemente incorrecto en el uso de flotantes como ID en JVM compatible con el estándar [*]. Si simplemente configura la ID del flotador en x, no haga nada con ella (es decir, sin aritmética) y luego pruebe y == x, estará bien. Además, no hay nada de malo en usarlos como claves en un HashMap. Lo que no puede hacer es asumir igualdades
x == (x - y) + y
, etc. Dicho esto, las personas usualmente usan tipos enteros como ID, y puede observar que la mayoría de las personas aquí están desanimadas por este código, por lo que, por razones prácticas, es mejor cumplir con las convenciones . Tenga en cuenta que hay tantosdouble
valores diferentes como largosvalues
, por lo que no gana nada con el usodouble
. Además, generar el "próximo ID disponible" puede ser complicado con los dobles y requiere cierto conocimiento de la aritmética de coma flotante. No vale la pena.Por otro lado, confiar en la igualdad numérica de los resultados de dos cálculos matemáticamente equivalentes es arriesgado. Esto se debe a los errores de redondeo y la pérdida de precisión al convertir de representación decimal a binaria. Esto ha sido discutido hasta la muerte en SO.
[*] Cuando dije "JVM compatible con el estándar" quería excluir ciertas implementaciones de JVM con daño cerebral. Mira esto .
fuente
==
lugar de hacerloequals
, o de lo contrario, asegurarse de que no se almacene en una tabla ningún flotante que se compare de forma desigual. De lo contrario, un programa que intenta, por ejemplo, contar cuántos resultados únicos se pueden producir a partir de una expresión cuando se alimentan varias entradas, puede considerar cada valor de NaN como único.Float
, no afloat
.Float
? Si uno intenta construir una tabla defloat
valores únicos y los compara==
, las horribles reglas de comparación IEEE-754 harán que la tabla se inunde deNaN
valores.float
El tipo no tieneequals
método.equals
método de instancia, sino al método estático (creo que dentro de laFloat
clase) que compara dos valores de tipofloat
.Este es un problema no específico de Java. Usar == para comparar dos flotantes / dobles / cualquier número de tipo decimal puede causar problemas debido a la forma en que están almacenados. Un flotador de precisión simple (según el estándar IEEE 754) tiene 32 bits, distribuidos de la siguiente manera:
1 bit - Signo (0 = positivo, 1 = negativo)
8 bits - Exponente (una representación especial (sesgo-127) de la x en 2 ^ x)
23 bits - Mantisa. El número actual que se almacena.
La mantisa es la que causa el problema. Es un poco como una notación científica, solo el número en la base 2 (binario) se parece a 1.110011 x 2 ^ 5 o algo similar. Pero en binario, el primer 1 es siempre un 1 (excepto la representación de 0)
Por lo tanto, para ahorrar un poco de espacio en la memoria (juego de palabras), IEEE decidió que se debe asumir el 1. Por ejemplo, una mantisa de 1011 realmente es 1.1011.
Esto puede causar algunos problemas con la comparación, especialmente con 0 ya que 0 no puede representarse exactamente en un flotante. Esta es la razón principal por la cual se desalienta el ==, además de los problemas matemáticos de coma flotante descritos por otras respuestas.
Java tiene un problema único en el sentido de que el lenguaje es universal en muchas plataformas diferentes, cada una de las cuales podría tener su propio formato flotante único. Eso hace que sea aún más importante evitar ==.
La forma correcta de comparar dos flotadores (sin lenguaje específico para usted) para la igualdad es la siguiente:
donde ACCEPTABLE_ERROR está #definido o alguna otra constante igual a 0.000000001 o cualquier precisión requerida, como ya mencionó Victor.
Algunos idiomas tienen esta funcionalidad o esta constante incorporada, pero generalmente es un buen hábito.
fuente
A partir de hoy, la forma rápida y fácil de hacerlo es:
Sin embargo, los documentos no especifican claramente el valor de la diferencia de margen (un épsilon de la respuesta de @Victor) que siempre está presente en los cálculos de los flotadores, pero debería ser algo razonable, ya que es parte de la biblioteca de idiomas estándar.
Sin embargo, si se necesita una precisión mayor o personalizada, entonces
Es otra opción de solución.
fuente
(sectionId == currentSectionId)
que no es preciso para los puntos flotantes. El método epsilon es el mejor enfoque, que se encuentra en esta respuesta: stackoverflow.com/a/1088271/4212710Los valores del punto de alimentación no son confiables debido al error de redondeo.
Como tal, probablemente no deberían usarse como valores clave, como sectionID. Utilice enteros en su lugar, o
long
siint
no contiene suficientes valores posibles.fuente
double
s son mucho más precisos, pero también son valores de coma flotante, por lo que mi respuesta estaba destinada a incluir ambosfloat
ydouble
.Además de las respuestas anteriores, debe tener en cuenta que hay comportamientos extraños asociados con
-0.0f
y+0.0f
(lo son==
pero no lo sonequals
) yFloat.NaN
(lo esequals
pero no lo es==
) (espero haberlo hecho bien, ¡argh, no lo haga!).Editar: ¡Vamos a ver!
Bienvenido a IEEE / 754.
fuente
==
no significa que los números sean "idénticos al bit" (el mismo número puede representarse con diferentes patrones de bits, aunque solo uno de ellos tiene forma normalizada). Además,-0.0f
y0.0f
están representados por diferentes patrones de bits (el bit de signo es diferente), pero se compara como igual con==
(pero no conequals
). Su suposición de que==
es una comparación bit a bit es, en general, errónea.Aquí hay una discusión muy larga (pero con suerte útil) sobre este y muchos otros problemas de coma flotante que puede encontrar: Lo que todo informático debe saber sobre la aritmética de coma flotante
fuente
Puede usar Float.floatToIntBits ().
fuente
En primer lugar, ¿son flotantes o flotantes? Si uno de ellos es flotante, debe usar el método equals (). Además, probablemente sea mejor usar el método estático Float.compare.
fuente
Lo siguiente usa automáticamente la mejor precisión:
Por supuesto, puede elegir más o menos de 5 ULP ('unidad en último lugar').
Si te gusta la biblioteca Apache Commons, la
Precision
clase tienecompareTo()
yequals()
con epsilon y ULP.fuente
double
cubrir esto.es posible que desee que sea ==, pero 123.4444444444443! = 123.4444444444442
fuente
Si * tiene que * usar flotantes, la palabra clave estrictafp puede ser útil.
http://en.wikipedia.org/wiki/strictfp
fuente
Dos cálculos diferentes que producen números reales iguales no necesariamente producen números iguales de coma flotante. Las personas que usan == para comparar los resultados de los cálculos generalmente terminan sorprendidos por esto, por lo que la advertencia ayuda a marcar lo que de otro modo sería un error sutil y difícil de reproducir.
fuente
¿Está tratando con código subcontratado que usaría flotantes para cosas llamadas sectionID y currentSectionID? Sólo curioso.
@ Bill K: "La representación binaria de un flotador es un poco molesta". ¿Cómo es eso? ¿Cómo lo harías mejor? Hay ciertos números que no se pueden representar en ninguna base correctamente, porque nunca terminan. Pi es un buen ejemplo. Solo puedes aproximarlo. Si tiene una mejor solución, comuníquese con Intel.
fuente
Como se menciona en otras respuestas, los dobles pueden tener pequeñas desviaciones. Y podría escribir su propio método para compararlos utilizando una desviación "aceptable". Sin embargo ...
Hay una clase apache para comparar dobles: org.apache.commons.math3.util.Precision
Contiene algunas constantes interesantes:
SAFE_MIN
yEPSILON
, que son las desviaciones máximas posibles de operaciones aritméticas simples.También proporciona los métodos necesarios para comparar, igualar o redondear dobles. (usando ulps o desviación absoluta)
fuente
En una línea de respuesta que puedo decir, debes usar:
Para que aprenda más sobre el uso correcto de operadores relacionados, estoy elaborando algunos casos aquí: Generalmente, hay tres formas de probar cadenas en Java. Puede usar ==, .equals () u Objects.equals ().
¿En qué se diferencian? == prueba la calidad de referencia en cadenas, lo que significa descubrir si los dos objetos son iguales. Por otro lado, .equals () comprueba si las dos cadenas tienen el mismo valor lógicamente. Finalmente, Objects.equals () prueba cualquier nulo en las dos cadenas y luego determina si se debe llamar a .equals ().
Operador ideal para usar
Bueno, esto ha sido objeto de muchos debates porque cada uno de los tres operadores tiene su conjunto único de fortalezas y debilidades. Ejemplo, == a menudo es una opción preferida cuando se compara la referencia de objeto, pero hay casos en los que puede parecer que también se comparan valores de cadena.
Sin embargo, lo que obtienes es un valor de caída porque Java crea una ilusión de que estás comparando valores pero en el sentido real no lo eres. Considere los dos casos a continuación:
Caso 1:
Caso 2:
Por lo tanto, es mucho mejor usar cada operador al probar el atributo específico para el que está diseñado. Pero en casi todos los casos, Objects.equals () es un operador más universal, por lo tanto, los desarrolladores web experimentados optan por él.
Aquí puede obtener más detalles: http://fluentthemes.com/use-compare-strings-java/
fuente
La forma correcta sería
fuente
Float.compare(1.1 + 2.2, 3.3) != 0
Una forma de reducir el error de redondeo es usar doble en lugar de flotante. Esto no hará que el problema desaparezca, pero reduce la cantidad de error en su programa y la opción flotante casi nunca es la mejor. EN MI HUMILDE OPINIÓN.
fuente