2010-10-05 17 views
35

En realidad, he encontrado posible solución¿Por qué BigDecimal ("5.50") no es igual a BigDecimal ("5.5") y cómo evitar este problema?

//returns true 
new BigDecimal ("5.50").doubleValue() == new BigDecimal("5.5").doubleValue() 

Por supuesto, se puede mejorar con algo como Math.abs (v1 - v2) < EPS para hacer la comparación más robusto, pero la pregunta es si esta técnica aceptable o hay una solución mejor?

Si alguien sabe por qué los diseñadores de Java decidieron implementar los equivalentes de BigDecimal de esa manera, sería interesante leerlo.

+7

Si sus objetos BigDecimal están garantizados para estar siempre representable mediante dobles, entonces no debería estar utilizando BigDecimal de todos modos. Si no lo son, entonces este método va a fallar. – DJClayworth

+2

Mala solución. Si los dobles son apropiados para su programa, use dobles. Si BigDecimals es apropiado, use BigDecimals. Casi nunca es útil convertir de ida y vuelta. – Jay

+0

@DJClayworth: ¿dónde ves la etiqueta "editada"? – Roman

Respuesta

68

Desde el javadoc de BigDecimal

es igual a

public boolean equals(Object x)

compara esta BigDecimal con el Object especificada por la igualdad. A diferencia de compareTo, este método considera que dos objetos BigDecimal equivalen a solo si son iguales en valor y escala (por lo tanto, 2.0 no es igual a 2.00 cuando se compara con este método).

Utilice simplemente compareTo() == 0

+0

....... ¿Pero por qué? – bacar

+0

Porque la implementación de 'igual' no solo se basa en el valor sino también en la escala, esto evita tener' new BigDecimal ("5.01"). Equal (nuevo BigDecimal ("5.0")) == falso' while ' nuevo BigDecimal ("5.0"). equals (nuevo BigDecimal ("5.01")) == true'. Todo eso porque ['equals()' es simétrico] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals (java.lang.Object)). –

+2

No necesita renunciar a la simetría para implementar 'iguales' como una comparación de valores numéricos, por lo que la simetría no es la razón. He preguntado explícitamente la pregunta "por qué" aquí: [¿Por qué se especifica BigDecimal.equals para comparar tanto el valor como la escala de forma individual?] (Http://stackoverflow.com/questions/14102083/why-is-bigdecimal-equals-specified -to-compare-both-value-and-scale-individualmente) – bacar

7

Usando == comparar dobles seems like a bad idea en general.

Se podría llamar a setScale lo mismo en los números que está comparando:

new BigDecimal ("5.50").setScale(2).equals(new BigDecimal("5.5").setScale (2)) 

donde sería la configuración de la escala a la mayor de las dos:

BigDecimal a1 = new BigDecimal("5.051"); 
BigDecimal b1 = new BigDecimal("5.05"); 
// wow, this is awkward in Java 
int maxScale = Collections.max(new ArrayList() {{ a1.scale(), b1.scale()}}); 
System.out.println(
    a1.setScale(maxScale).equals(b1.setScale(maxScale)) 
    ? "are equal" 
    : "are different"); 

Usando compareTo() == 0 es la mejor respuesta, sin embargo. La cada vez mayor de la escala de uno de los números en mi acercamiento anterior es probable que la "inflación innecesaria" que la documentación del método compareMagnitude está mencionando cuando dice:

/** 
* Version of compareTo that ignores sign. 
*/ 
private int compareMagnitude(BigDecimal val) { 
    // Match scales, avoid unnecessary inflation 
    long ys = val.intCompact; 
    long xs = this.intCompact; 

y por supuesto compareTo es mucho más fácil de usar ya ya está implementado para ti.

+6

solo haga esto si quiere considerar "5.051" == "5.05" como setScale (2) soltará ese dígito adicional, donde como .compareTo (otro) comparará los valores sin tener en cuenta la escala –

+3

Gareth, 'setScale' sin un parámetro de modo de redondeo no caerá los dígitos adicionales, en su lugar arrojará una ArithmeticException. Entonces esto solo funcionaría si los dígitos extra son todos ceros. –

+0

@Gareth: gracias por los comentarios. en el momento en que escribí esto hace mucho tiempo, no recibí comentarios ni notificaciones, así que me perdí esto. finalmente notó esto y actualizó tomando en cuenta su comentario. –

4

la expresión más simple de comparar haciendo caso omiso de ceros a la izquierda es desde Java 1.5:

bd1.stripTrailingZeros().equals(bd2.stripTrailingZeros())