2011-01-04 6 views

Odpowiedz

647

Z Java 5 docs (Java 8 docs here):

Kiedy obiekt MathContext dostarczany jest z ustawieniem precyzyjnym od 0 (dla przykład MathContext.UNLIMITED), operacje arytmetyczne są dokładne, ponieważ są metodami arytmetycznymi, które nie pobierają żadnego obiektu MathContext. (Jest to tylko zachowanie, które zostały w wersji przed 5)

Jako następstwo obliczania dokładnego wyniku, ustawienie trybu zaokrąglanie MathContext obiektu z ustawieniem precyzyjnego 0 nie jest używany, a zatem bez znaczenia. W przypadku podziału, dokładny iloraz może mieć nieskończenie długą wartość dziesiętną po rozwinięciu; na przykład 1 podzielone przez 3.

Jeśli iloraz ma nierozszerzającą liczbę dziesiętną i , operacja jest określona w celu zwrócenia dokładnego wyniku, generowany jest wyjątek arytmetyczny. W przeciwnym razie zwracany jest dokładny wynik podziału, tak jak w przypadku innych operacji.

Aby naprawić, trzeba zrobić coś takiego:

a.divide(b, 2, RoundingMode.HALF_UP) 

where 2 is precision and RoundingMode.HALF_UP is rounding mode 

Więcej szczegółów: http://jaydeepm.wordpress.com/2009/06/04/bigdecimal-and-non-terminating-decimal-expansion-error/

+2

Nie sądzę, że potrzebuje 'toPlainString'. –

+2

działa to również w przypadku błędów jaspisowych, ponieważ http://community.jaspersoft.com/questions/528968/help-please-adding-twoja-dwa_dwóch-wartości #comment-807628 – shareef

+15

2 NIE jest "precyzją"; to jest 'scale'. Zobacz http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#divide%28java.math.BigDecimal,%20int,%20java.math.RoundingMode%29 –

68

bo nie jesteś określenie precyzji i zaokrąglania-mode. BigDecimal skarży się, że może użyć miejsc dziesiętnych 10, 20, 5000 lub nieskończoności, ale nadal nie będzie w stanie podać dokładnej liczby. Więc zamiast dawać ci niepoprawny BigDecimal, po prostu się do ciebie odzywa.

Jeśli jednak podasz RoundingMode i precyzję, to będzie on w stanie przekonwertować (np. 1.333333333-to-infinity na coś podobnego do 1.3333 ... ale ty jako programista musisz powiedzieć, jakiej precyzji jesteś " re „zadowolony z”.

5

miałem ten sam problem, ponieważ mój wiersz kodu było:

txtTotalInvoice.setText(var1.divide(var2).doubleValue() + ""); 

mogę zmienić tego, czytając poprzednią odpowiedź, bo byłem nie zapisując dokładności dziesiętnej:

txtTotalInvoice.setText(var1.divide(var2,4, RoundingMode.HALF_UP).doubleValue() + ""); 

4 jest dziesiętny Precion

I RoundingMode są Enum stałe, można wybrać dowolny z tym UP, DOWN, CEILING, FLOOR, HALF_DOWN, HALF_EVEN, HALF_UP

w tym przypadku HALF_UP, będzie miał ten wynik:

2.4 = 2 
2.5 = 3 
2.7 = 3 

Można sprawdzić informacje RoundingMode tutaj : http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/

10

Do naprawy takiego problemu użyłem poniższego kodu:

a.divide(b, 2, RoundingMode.HALF_EVEN) 

2 to precyzja. Teraz problem został rozwiązany.

+3

dodatkowo do kodu, należy podać wyjaśnienie. –

+6

2 NIE jest "precyzją"; to jest 'scale'. Zobacz http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#divide%28java.math.BigDecimal,%20int,%20java.math.RoundingMode%29 –

+1

RoundingMode. HALF_EVEN jest zalecany do aplikacji finansowych. To jest to, co jest używane w bankowości – ACV

1

odpowiedzi na BigDecimal throws ArithmeticException

public static void main(String[] args) { 
     int age = 30; 
     BigDecimal retireMentFund = new BigDecimal("10000.00"); 
     retireMentFund.setScale(2,BigDecimal.ROUND_HALF_UP); 
     BigDecimal yearsInRetirement = new BigDecimal("20.00"); 
     String name = " Dennis"; 
     for (int i = age; i <=65; i++){ 
      recalculate(retireMentFund,new BigDecimal("0.10")); 
     } 
     BigDecimal monthlyPension = retireMentFund.divide(
       yearsInRetirement.divide(new BigDecimal("12"), new MathContext(2, RoundingMode.CEILING)), new MathContext(2, RoundingMode.CEILING));  
     System.out.println(name+ " will have £" + monthlyPension +" per month for retirement"); 
    } 
public static void recalculate (BigDecimal fundAmount, BigDecimal rate){ 
     fundAmount.multiply(rate.add(new BigDecimal("1.00"))); 
    } 

Dodaj obiekt MathContext w podzielić wywołanie metody i dostosować precyzji i tryb zaokrąglania. To powinno naprawić twój problem.

0

Jest tak dlatego, że Bigdecimal nie został utracony, a jeśli podzielisz na przykład 1/3, spowoduje to powtarzanie dziesiętnych do nieskończoności. 0.33333333 ... Teoretycznie, jeśli pomnożysz wynik, otrzymasz dokładny wynik. Jednak numer nieskończoności generuje stos nad przepływie i w tym przypadku uruchamiany jest wyjątek.

Moje rozwiązanie:

try { 
    result = n1.divide(n2); 
} catch (ArithmeticException e){ 
    Log.d("Error bigdecimal", e.toString()); 
    result = (n1.doubleValue()/n2.doubleValue()); 
}; 

w tym wypadku wynik nie zostanie pocięty przez zaokrąglenie

+2

To jest "gambiarra". Proszę użyć rozwiązania z RoundingMode. –

Powiązane problemy