2010-10-05 1 views
35

На самом деле, я нашел возможное решениеПочему BigDecimal («5.50») не равен BigDecimal («5.5») и как обойти эту проблему?

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

Конечно, это может быть улучшено с чем-то вроде Math.abs (v1 - v2) < EPS сделать сравнение более надежным, но вопрос в том, является ли эта методика приемлема или есть лучшее решение?

Если кто-то знает, почему дизайнеры java решили реализовать равные значения BigDecimal, было бы интересно прочитать.

+7

Если ваши объекты BigDecimal гарантированно будут всегда представима двойников, то вы не должны использовать BigDecimal в любом случае. Если это не так, то этот метод будет терпеть неудачу. – DJClayworth

+2

Плохое решение. Если двойники подходят для вашей программы, используйте удвоения. Если BigDecimals подходят, используйте BigDecimals. Почти никогда не бывает полезно конвертировать туда и обратно. – Jay

+0

@DJClayworth: где вы видите «отредактированный» ярлык? – Roman

ответ

68

От Javadoc из BigDecimal

равна

public boolean equals(Object x)

Сравнивает этот BigDecimal с указанным Object для равенства. В отличие от compareTo, этот метод рассматривает два объекта BigDecimal равными , только если они равны по значению и шкале (при этом 2.0 не сравнивается с 2.00 по сравнению с этим методом).

Просто используйте compareTo() == 0

+0

....... Но почему? – bacar

+0

Поскольку реализация «equals» основана не только на значении, но и на масштабе, это позволяет избежать «нового BigDecimal (« 5.01 »). Equals (new BigDecimal (« 5.0 »)) == false' while' новый BigDecimal ("5.0"). equals (новый BigDecimal ("5.01")) == true'. Все это, потому что ['equals()' является симметричным) (http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals (java.lang.Object)). –

+2

Вам не нужно отказываться от симметрии, чтобы реализовать 'equals' как сравнение числовых значений, поэтому симметрия не является причиной. Я задал здесь вопрос «почему»: [Почему BigDecimal.equals указано для сравнения как значения, так и масштаба отдельно?] (Http://stackoverflow.com/questions/14102083/why-is-bigdecimal-equals-specified -to-compare-both-value-and-scale-individual) – bacar

7

== Использование для сравнения двойников seems like a bad idea в целом.

Вы могли бы назвать setScale к тому же на номера вы сравниваете:

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

, где вы бы установка масштаба к большему из двух:

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"); 

Использование compareTo() == 0 это лучший ответ. Увеличение масштаба одного из чисел в моем подходе выше, скорее всего, «ненужная инфляция», что метод документация compareMagnitude упоминает, когда он говорит:

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

и конечно compareTo намного проще в использовании, поскольку это уже реализовано для вас.

+6

только сделайте это, если вы хотите рассмотреть «5.051» == «5.05», поскольку setScale (2) сбросит эту дополнительную цифру, где в качестве .compareTo (other) будет сравнивать значения без учета масштаба –

+3

Gareth, 'setScale' без параметра режима округления, не будет выпадать лишние цифры, вместо этого он выкинет исключение ArithmeticException. Таким образом, это будет работать, только если дополнительные цифры равны нулю. –

+0

@ Gareth: спасибо за отзыв. в то время, когда я писал это давно, я не был связан с комментариями и уведомлениями, поэтому я пропустил это. наконец, заметил это и обновил ваш комментарий. –

4

самое простое выражение для сравнения, игнорирующих ведущих нулей есть с Java 1.5:

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