Java: Comparación de doble valor

¿Debemos ser cuidadosos al comparar un valor doble contra cero?

if ( someAmount <= 0){ ..... } 

Si quieres ser realmente cuidadoso, puedes probar si está dentro de un épsilon de cero con algo así como

 double epsilon = 0.0000001; if ( f <= ( 0 - epsilon ) ) { .. } else if ( f >= ( 0 + epsilon ) ) { .. } else { /* f "equals" zero */ } 

O simplemente puede redondear sus dobles con una precisión específica antes de ramificarse en ellos.

Para obtener algunos detalles interesantes sobre cómo comparar el error en los números de punto flotante, aquí hay un artículo de Bruce Dawson .

Para la igualdad: (es decir, == o != ) Sí.

Para los otros operadores comparativos ( < , > , <= , >= ), depende de si se trata de casos extremos, por ejemplo, si < es equivalente a <= , que es otro caso de igualdad. Si no te importan los casos extremos, generalmente no importa, aunque depende de dónde provengan tus números de entrada y cómo se usan.

Si espera que (3.0/10.0) <= 0.3 evalúe como true (puede no hacerlo si el error de coma flotante hace que 3.0 / 10.0 lo evalúe a un número ligeramente mayor que 0.3 como 0.300000000001), y su progtwig se comportará mal si evalúa como false - eso es un caso extremo, y debe tener cuidado.

Los buenos algoritmos numéricos casi nunca deberían depender de la igualdad y los casos extremos. Si tengo un algoritmo que toma como entrada ' x ' que es cualquier número entre 0 y 1, en general no debería importar si 0 < x < 1 o 0 <= x <= 1 . Sin embargo, hay excepciones: debe tener cuidado al evaluar funciones con puntos de ramificación o singularidades.

Si tengo una cantidad intermedia y y estoy esperando y >= 0 , y evalúo sqrt(y) , entonces tengo que estar seguro de que los errores de punto flotante no causan que y sea un número negativo muy pequeño y el sqrt() función para lanzar un error. (Suponiendo que esta es una situación donde los números complejos no están involucrados.) Si no estoy seguro acerca del error numérico, probablemente evaluaría sqrt(max(y,0)) lugar.

Para expresiones como 1/y o log(y) , en un sentido práctico, no importa si y es cero (en cuyo caso se obtiene un error de singularidad) o y es un número muy cercano a cero (en cuyo caso sacar un número muy grande, cuya magnitud es muy sensible al valor de y) - ambos casos son "malos" desde un punto de vista numérico, y necesito reevaluar qué estoy tratando de hacer, y qué comportamiento Estoy buscando cuando los valores y están cerca de cero.

Dependiendo de cómo se someAmount su someAmount , puede esperar algún comportamiento extraño con flotante / doble

Básicamente, la conversión de datos numéricos a su representación binaria utilizando float / dobles es propenso a errores, porque algunos números no se pueden representar con un mantis / exponente.

Para algunos detalles sobre esto puedes leer este pequeño artículo

Debe considerar usar java.lang.Math.signum o java.math.BigDecimal , especialmente para la informática de monedas e impuestos.

Cuidado con el auto-unboxing:

 Double someAmount = null; if ( someAmount <= 0){ 

Boom, NullPointerException.

Sí, debes tener cuidado.

Sugerencia: una de las mejores sería usar BigDecimal para verificar igualdad / no igualdad a 0:

 BigDecimal balance = pojo.getBalance();//get your BigDecimal obj 0 != balance.compareTo(BigDecimal.ZERO) 

Explicación

La función compareTo() compara este BigDecimal con el BigDecimal especificado. Dos objetos BigDecimal que son iguales en valor pero tienen una escala diferente (como 2.0 y 2.00) se consideran iguales por este método. Este método se proporciona con preferencia a los métodos individuales para cada uno de los seis operadores de comparación boolean (<, ==, >, >=, !=, <=) . El modismo sugerido para realizar estas comparaciones es: (x.compareTo(y) 0) , donde es uno de los seis operadores de comparación.

(Gracias a la documentación de SonarQube)

La matemática de coma flotante es imprecisa debido a los desafíos de almacenar dichos valores en una representación binaria. Peor aún, las matemáticas de punto flotante no son asociativas; empujar un flotador o un doble a través de una serie de operaciones matemáticas simples y la respuesta será diferente en función del orden de esas operaciones debido al redondeo que tiene lugar en cada paso.

Incluso las asignaciones simples de punto flotante no son simples:

 float f = 0.1; // 0.100000001490116119384765625 double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625 

(Los resultados variarán según el comstackdor y la configuración del comstackdor);

Por lo tanto, el uso de los operadores de igualdad (==) y desigualdad (! =) En valores flotantes o dobles es casi siempre un error. En cambio, lo mejor es evitar por completo las comparaciones de coma flotante. Cuando eso no sea posible, debería considerar usar uno de los números de manejo de flotación de Java, como BigDecimal, que puede manejar adecuadamente las comparaciones de coma flotante. Una tercera opción es mirar no por la igualdad sino por si el valor es lo suficientemente cercano. Es decir, compare el valor absoluto de la diferencia entre el valor almacenado y el valor esperado con un margen de error aceptable. Tenga en cuenta que esto no cubre todos los casos (NaN e Infinity, por ejemplo).

Si no te importan los casos someAmount <= 0 prueba con someAmount <= 0 . Hace que la intención del código sea clara. Si te importa, bueno ... depende de cómo someAmount y por qué pruebas la desigualdad.

Verifique que el valor doble o flotante sea 0, se usa un umbral de error para detectar si el valor está cerca de 0, pero no exactamente 0. Creo que este método es el mejor que encontré.

¿Cómo probar si un doble es cero? Respondido por @William Morrison

 public boolean isZero(double value, double threshold){ return value >= -threshold && value <= threshold; } 

Por ejemplo, configure el umbral como 0. así,

 System.out.println(isZero(0.00, 0)); System.out.println(isZero(0, 0)); System.out.println(isZero(0.00001, 0)); 

Los resultados son verdaderos, verdaderos y falsos de los códigos de ejemplo anteriores.

Que te diviertas @.@