2010-10-05 13 views
35

Faktycznie, Znalazłem możliwe rozwiązanieDlaczego BigDecimal ("5,50") nie jest równy BigDecimal ("5,5") i jak obejść ten problem?

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

oczywiście, można go poprawić coś podobnego Math.abs (v1 - v2) < EPS dokonać porównania bardziej wytrzymałe, ale pytanie brzmi, czy tej techniki dopuszczalne lub tam jest lepszym rozwiązaniem?

Jeśli ktoś wie, dlaczego projektanci java zdecydowali się na wdrożenie w ten sposób równań BigDecimal, byłoby to interesujące przeczytać.

+7

Jeśli BigDecimal obiekty są gwarancją zawsze reprezentowalna przez deblu, to nie powinno być przy użyciu BigDecimal tak. Jeśli nie, to ta metoda zakończy się niepowodzeniem. – DJClayworth

+2

Złe rozwiązanie. Jeśli liczba podwójna jest odpowiednia dla twojego programu, używaj podwójnych. Jeśli BigDecimals są odpowiednie, użyj BigDecimals. Prawie nigdy nie jest użyteczne konwersja w tę iz powrotem. – Jay

+0

@DJClayworth: gdzie widzisz etykietę "edytowaną"? – Roman

Odpowiedz

68

Z javadoc z BigDecimal

równa

public boolean equals(Object x)

Porównuje tę BigDecimal z określonym Object równości. W przeciwieństwie do compareTo, ta metoda traktuje dwa obiekty równe tylko wtedy, gdy są one równe pod względem wartości i skali (w związku z tym 2,0 nie jest równe 2,00 w porównaniu z tą metodą).

Wystarczy użyć compareTo() == 0

+0

....... Ale dlaczego? – bacar

+0

Ponieważ implementacja 'equals' nie jest oparta tylko na wartości, ale także na skali, unika się tego, że' new BigDecimal ("5.01") jest równy (new BigDecimal ("5.0")) == false' while ' nowy BigDecimal ("5.0"). equals (nowy BigDecimal ("5.01")) == true'. Wszystko dlatego, że ['równa się()' jest symetryczna] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals (java.lang.Object)). –

+2

Nie musisz rezygnować z symetrii, aby zaimplementować 'equals' jako porównanie wartości numerycznych, więc symetria nie jest powodem. Pytałem wyraźnie pytanie "dlaczego" w tym miejscu: [Dlaczego metoda BigDecimal.equals jest używana do porównywania wartości i skalowania indywidualnie?] (Http://stackoverflow.com/questions/14102083/why-is-bigdecimal-equals-specified -to-porównać-zarówno-wartość-i-skalowanie-pojedynczo) – bacar

7

Korzystanie == porównać podwaja seems like a bad idea w ogóle.

Można zadzwonić setScale do tej samej rzeczy na numery jesteś porównywanie:

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

gdzie można byłoby ustawienie skali większej z dwóch:

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

Korzystanie compareTo() == 0 jest najlepszą odpowiedzią. Zwiększaniu skali jednego z numerów w moim podejściu powyżej jest prawdopodobne, że „niepotrzebne inflacja”, że dokumentacja metoda compareMagnitude jest wspomnieć, kiedy mówi:

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

i oczywiście compareTo jest dużo łatwiejszy w obsłudze od jest już zaimplementowany.

+6

rób to tylko jeśli chcesz wziąć pod uwagę "5.051" == "5.05", ponieważ setScale (2) upuści tę dodatkową cyfrę, gdzie jako .compareTo (inne) porówna wartości bez względu na skalę –

+3

Gareth, 'setScale' bez parametru zaokrąglania nie spadnie dodatkowych cyfr, zamiast tego rzuci wyjątek ArithmeticException. Tak więc działałoby to tylko wtedy, gdy dodatkowe cyfry są zerami. –

+0

@Gareth: dzięki za opinię. w czasie, kiedy pisałem to dawno temu, nie byłem wpuszczony w komentarze i powiadomienia, więc tęskniłem za tym. wreszcie to zauważyłem i zaktualizowałem biorąc pod uwagę twój komentarz. –

4

najprostsze wyrażenie porównać ignorując zer jest od Java 1.5:

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