2012-04-08 48 views
9

Mój program w języku Java koncentruje się na obliczeniach o wysokiej precyzji, które muszą być dokładne z dokładnością do co najmniej 120 miejsc dziesiętnych.
W konsekwencji wszystkie liczby niecałkowite będą reprezentowane przez BigDecimals w programie.Ustaw wszystkie operacje BigDecimal na określoną precyzję?

Oczywiście trzeba określić dokładność zaokrąglania dla BigDecimals, aby uniknąć nieskończonych wyrażeń dziesiętne itd
Obecnie uważam, że ogromną uciążliwość mieć określić dokładność w każdej instancji lub operacji matematycznej z BigDecimal .

Czy istnieje sposób na ustawienie "globalnej dokładności" dla wszystkich obliczeń BigDecimal?
(takie jak Context.prec() dla dziesiętny modułu w Pythonie)

Dzięki


Specyfikacja:
Java jre7 SE
systemu Windows 7 (32)

Odpowiedz

8

(Prawie) Oryginalny

nie jako proste, ale można utworzyć MathContext i przekazać go do wszystkich swoich BigDecimal konstruktorów i metod wykonujących operacje.

Revised

Alternatywnie, można przedłużyć BigDecimal i zastępują wszelkie operacje, których chcesz użyć poprzez dostarczanie prawo MathContext i używając metody zaokrąglania wersję divide:

public class MyBigDecimal extends BigDecimal { 

     private static MathContext context = new MathContext(120, RoundingMode.HALF_UP); 

     public MyBigDecimal(String s) { 
      super(s, context); 
     } 
     public MyBigDecimal(BigDecimal bd) { 
      this(bd.toString()); // (Calls other constructor) 
     } 
     ... 
     public MyBigDecimal divide(BigDecimal divisor){ 
      return new MyBigDecimal(super.divide(divisor, context)); 
     } 
     public MyBigDecimal add(BigDecimal augend){ 
      return new MyBigDecimal(super.add(augend)); 
     } 
     ... 
} 
+0

Dokładnie tego chcę uniknąć. –

+0

W rzeczywistości mam trudności z używaniem rozszerzenia klasy. Jeśli mam 2 BigDecimals reprezentujące "1" i "3", z których oba mają skalę ustawioną na 120 ... Dzielenie "1" przez "3" nadal daje dziesiętny nie kończący się, nawet jeśli przypiszę wynik w skali z 120 również! (To było to, czego początkowo próbowałem uniknąć, dodatkowe parametry do operacji matematycznych). –

+0

Myślę, że możesz to naprawić, zastępując metodę "dziel" w swojej klasie. – trutheality

-3

można użyć BigDecimal setScale function!

BigDecimal db = new BigDecimal(<number>).setScale(120, BigDecimal.ROUND_HALF_UP); (or down) 
+0

Nadal musiałbym to robić przy każdym wystąpieniu BigDecimal. –

2

można utworzyć klasa, która rozszerza BigDecimal i automatycznie ustawia precyzję dla ciebie. Potem po prostu skorzystasz z tej klasy.

public class MyBigDecimal extends BigDecimal { 
     public MyBigDecimal(double d) { 
      super(d); 
      this.setScale(120, BigDecimal.ROUND_HALF_UP); 
     } 
     ... 
} 
+4

Działa to tylko wtedy, gdy opakowanie otacza wszystkie operacje BigDecimal, które tworzą nowe wystąpienie. –

+2

To przyniesie pracę. Z JavaDocs na "setScale": "Zauważ, że ponieważ obiekty BigDecimal są niezmienne, wywołania tej metody nie powodują modyfikacji oryginalnego obiektu, w przeciwieństwie do zwykłej konwencji posiadania metod o nazwie setX mutate field X. Zamiast tego, setScale zwraca obiekt o właściwej skali, zwracany obiekt może być lub nie być nowo przydzielony. " Więc wywołanie go w konstruktorze nic nie da, przynajmniej w niektórych sytuacjach. – SatA

0

Można utworzyć otoki dla BigDecimals, która zrobi to zadanie:

class BigDecimalWrapper { 
    BigDecimal bd; 

    BigDecimalWrapper add(BigDecimalWrapper another) { 
     BigDecimal r = this.bd.add(another.bd); 
     r.setScale(...); 
     bd = r; 
     return this; 
    } 
    // and so on for other operations 
} 

W tym przypadku nie trzeba zastąpić wszystkie działania BigDecimal (w przypadku powiększenia), tylko te, używasz. Daje kontrolę nad wszystkimi instancjami i nie wymusza przestrzegania umowy BigDecimal.

3

Utwórz klasę za pomocą statycznych metod fabrycznych pasujących do wszystkich konstruktorów akceptujących MathContext - z tą różnicą, że instancja MathContext znajduje się w fabryce i jest statycznie inicjowana przy uruchomieniu. Oto fragment:

public class BigDecimalFactory { 
    public static BigDecimal newInstance (BigInteger unscaledVal, int scale) { 
     return new BigDecimal (unscaledVal, scale, _mathContext); 
    } 

    // . . . other factory methods for other BigDecimal constructors 

    private static final MathContext _mathContext = 
     new MathContext (120, BigDecimal.ROUND_HALF_UP); 
} 
+0

To nie zadziała ... jeśli poprawnie odczytam javadoc. Głównym powodem jest to, że używany konstruktor używa "MathContext" tylko do konwersji. Nie jest on dołączony do wynikowego obiektu "BigDecimal". –

+0

Oznacza to, że ograniczenie dokładności nie będzie miało zastosowania do operacji wykonywanych na instancjach "BigDecimal" utworzonych w fabryce ... chyba że podasz 'MathContext' jako argument do odpowiedniej operacji. –

+0

Argh! Masz rację. Wspomniałem twoje komentarze w jednym z pozostałych wątków w tym poście, aby inni wiedzieli. –

2

Czy istnieje sposób, aby ustawić „Globalny dokładność” dla wszystkich obliczeń BigDecimal?

nr

Musisz utworzyć klasy otoki, który ma MathContext jako dodatkowy atrybut. Będzie trzeba:

  • stosowania tego mc dla każdej operacji matematycznej, które inaczej byłoby użyć domyślnego semantykę i

  • tworzyć i za każdym razem, owinięty operacja zwraca instancję regularne powrócić innym owinięty instancji.

(Jako odmianę, można zaimplementować „globalny” MathContext użyciu statycznej, ale nadal będziesz musiał użyć wrappering aby upewnić się, że mc jest używany).

(Rozszerzenie BigDecimal byłoby . działa zbyt, i to jest dyskusyjne neater niż klasy otoki)


Powiedziałeś to w komentarzu:

Naprawdę nie chcę pisać własnego modułu dziesiętnego, chcę tylko zrozumieć, dlaczego BigDecimal jest tak niechętny do współpracy.

(kwestie konstrukcyjne można odpowiedzieć tylko definitywnie przez zespół projektowy. Jednak ...)

jak w przypadku wszystkich skomplikowanych klas użytkowych, projekt BigDecimal jest kompromisem, który został zaprojektowany, aby sprostać wymagania szerokiej gamy zastosowań. Jest to również kompromis pomiędzy konkurującymi meta-wymaganiami (niewłaściwym słowem) "potęgi" i "prostoty".

To, co masz, to przypadek użycia, który nie jest szczególnie dobrze obsługiwany. Ale podejrzewam, że gdyby był dobrze obsługiwany (na przykład globalnym MathContext kontrolującym wszystko lub MathContext dołączonym do każdego z nich), wówczas wprowadziłoby to różnego rodzaju inne komplikacje; na przykład obsługa operacji, w których biorą udział dwa lub więcej konkurujących obiektów kontekstowych. Takie problemy można rozwiązać ... ale mogą doprowadzić do "niespodzianek" dla programisty, a to nie jest dobre.

Obecne podejście jest proste i łatwe do zrozumienia, a jeśli potrzebujesz czegoś bardziej skomplikowanego, możesz go wdrożyć ... poprzez jawne dostarczenie MathContext dla operacji, które tego wymagają.

Powiązane problemy