2012-08-29 15 views
6

Możliwe duplikaty:
ArithmeticException thrown during BigDecimal.divideDzielenie BigDecimals skutkuje ArithmeticException

Wynika to w ArithmeticException: http://ideone.com/RXeZw

Zapewnienie skalę i tryb zaokrąglania da mi zły wynik. Ten przykład powinien dać wynik 50,03%. Jak zaokrąglić to poprawnie?

Dla łatwiejszego odniesienia, jest to kod:

BigDecimal priceDiff = BigDecimal.ONE 
    .subtract(new BigDecimal(9.99) 
    .divide(new BigDecimal(19.99))) 
    .multiply(new BigDecimal(100)); 
System.out.println(priceDiff.toPlainString()); 

Odpowiedz

9

BigDecimal.divide(BigDecimal divisor) zgłasza ArithmeticException jeśli wynik nie może być dokładnie reprezentowana.
Będziesz musiał użyć podaj MathContext lub RoundingMode, informując, w jaki sposób chcesz sobie z tym poradzić. Na przykład:

public class Test { 
    public static void main(String[] args) throws java.lang.Exception { 
     BigDecimal priceDiff = BigDecimal.ONE.subtract(new BigDecimal("9.99").divide(new BigDecimal("19.99"), MathContext.DECIMAL128)) 
              .multiply(new BigDecimal(100)); 
     System.out.println(priceDiff.toPlainString()); 
    } 
} 

prace i druki

50.0250125062531265632816408204102100 

również pamiętać, korzystanie z BigDecimal(String) constructor aby uniknąć problemów podczas tworzenia BigDecimal użyciem double dosłowne.

4

Należy użyć MathContext jako podziału jako nieskończonej liczby miejsc po przecinku:

BigDecimal priceDiff = BigDecimal.ONE 
      .subtract(new BigDecimal(9.99) 
      .divide(new BigDecimal(19.99), new MathContext(10, RoundingMode.UP))) 
      .multiply(new BigDecimal(100)); 
    System.out.println(priceDiff.toPlainString()); 

jednak, że drukuje 50.025 ...

Byłoby to wydrukować 49.98:

BigDecimal priceDiff = new BigDecimal(9.99) 
     .divide(new BigDecimal(19.99), new MathContext(10, RoundingMode.UP)) 
     .multiply(new BigDecimal(100), new MathContext(4, RoundingMode.UP)); 
System.out.println(priceDiff.toPlainString()); 
1

Operacje BigDecimal zawsze próbują obliczyć dokładny wynik. Spróbuj tego.

BigDecimal priceDiff = BigDecimal.ONE 
       .subtract(new BigDecimal(9.99) 
         .divide(new BigDecimal(19.99),RoundingMode.HALF_UP)) 
       .multiply(new BigDecimal(100)); 
     System.out.println(priceDiff.toPlainString()); 
1

Podobny do użytkownika @ Keppil odpowiedź

double d = (1 - 9.99/19.99) * 100; 
System.out.printf("%.2f%%%n", d); 

drukuje

50.03% 

nie jesteś dostanie 49,98%, jak to nie jest odpowiedź na twoje równania.

+0

Tak, właśnie pomyliłem się o tych wszystkich liczbach :-) – dtrunk

+0

@dtrunk Używam "podwójnego" mniej mylącego. ;) –

+0

Nie mogę użyć podwójnego, ponieważ spowodowałoby to tyle zmian w kodzie – dtrunk

2

Z Javadoc:

W przypadku podziału dokładna iloraz może mieć nieskończenie długi rozszerzenie po przecinku; na przykład, 1 podzielony przez 3. Jeśli iloraz ma niekontrolowane dziesiętne rozwinięcie i operacja jest określona w celu zwrócenia dokładnego wyniku, generowany jest wyjątek ArithmeticException.

Rozwiązaniem jest zapewnienie MathContext podziału, na przykład MathContext.DECIMAL128.Jeśli chcesz zaokrąglenia wykonane konwencjonalny sposób (zaokrąglania połówki się raczej niż najbliższa liczba parzysta), użyj MathContext(int precision)

Należy również być tworzenia BigDecimal sz String argumentów zamiast double argumentów, ponieważ straty ostatnie przyczyny precyzją .