2012-04-08 10 views
9

Mein Java-Programm konzentriert sich auf Berechnungen mit hoher Genauigkeit, die auf mindestens 120 Dezimalstellen genau sein müssen.
Folgerichtig werden alle nicht ganzzahligen Zahlen im Programm durch BigDecimals repräsentiert.Alle BigDecimal-Operationen auf eine bestimmte Genauigkeit einstellen?

Offensichtlich brauche ich die Genauigkeit der Rundung für die BigDecimals angeben, Ausdrücke unendliche Dezimalzahl zu vermeiden usw.
Zur Zeit finde ich es eine massive Beeinträchtigung der Genauigkeit bei jeder Instanzierung oder mathematische Operation eines BigDecimal angeben müssen .

Gibt es eine Möglichkeit, eine "globale Genauigkeit" für alle BigDecimal-Berechnungen festzulegen?
(wie die Context.prec() für das Dezimal Modul in Python)

Dank


Specs:
Java jre7 SE
von Windows 7 (32)

Antwort

8

(Fast) Original

nicht so einfach, aber Sie können einen MathContext erstellen und übergeben ihn an alle Ihre BigDecimal Konstrukteure und die Methoden Durchführung von Operationen.

Überarbeitete

Alternativ Sie BigDecimal erweitern können und überschreiben alle Operationen, die Sie, indem Sie die richtigen MathContext, und mit Hilfe der Rundungs ​​Version von divide verwenden möchten:

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

Genau das möchte ich vermeiden. –

+0

Ich habe tatsächlich einige Schwierigkeiten mit der Klassenerweiterung. Wenn ich 2 BigDecimals habe, die "1" und "3" repräsentieren, haben beide eine Skalierung von 120 ... Teilen von "1" durch "3" erzeugt immer noch eine nicht endende Dezimalzahl, auch wenn ich dem Ergebnis eine Skalierung zuteile von 120 auch! (Was ich ursprünglich vermeiden wollte; zusätzliche Parameter für mathematische Operationen). –

+0

Ich denke, dass Sie das beheben können, indem Sie die Divide-Methode für Ihre Klasse ähnlich überschreiben. – trutheality

-3

können Sie die Verwendung BigDecimal setScale-Funktion!

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

Ich würde dies immer noch bei jeder Instantiierung eines BigDecimal durchführen müssen. –

2

Sie könnten ein erstellen Klasse, die BigDecimal erweitert und die Genauigkeit automatisch für Sie einstellt. Dann benutzt du einfach diese Klasse.

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

Dies funktioniert nur, wenn der Wrapper auch alle BigDecimal-Operationen umschließt, die eine neue Instanz erstellen. –

+2

Das wird nicht funktionieren. Von den JavaDocs auf 'setScale': "Da BigDecimal-Objekte unveränderbar sind, bewirken Aufrufe dieser Methode nicht, dass das ursprüngliche Objekt geändert wird. Im Gegensatz zu der üblichen Konvention, Methoden namens setX zu verwenden, mutieren Sie Feld X. Stattdessen gibt setScale ein Objekt mit der richtigen Skalierung; das zurückgegebene Objekt kann neu zugewiesen werden oder nicht. " Wenn man es im Konstruktor aufruft, wird es zumindest in manchen Situationen nichts tun. – SatA

0

Sie einen Wrapper für BigDecimals erstellen können, die diesen Job machen wird:

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 
} 

In diesem Fall müssen Sie nicht alle Betrieb von BigDecimal außer Kraft setzen (bei Verlängerung), um nur diejenigen Sie nutzen. Es gibt Ihnen die Kontrolle über alle Instanzen und zwingt nicht, BigDecimal-Vertrag zu folgen.

3

Erstellen Sie eine BigDecimalFactory Klasse mit statischen Factory-Methoden, die mit allen Konstruktoren übereinstimmen, die MathContext akzeptieren - mit der Ausnahme, dass die MathContext Instanz innerhalb der Factory liegt und beim Start automatisch initialisiert wird. Hier ist ein 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

Das wird nicht funktionieren ... wenn ich das Javadoc richtig gelesen habe. Der Hauptgrund ist, dass der Konstruktor, den Sie verwenden, den 'MathContext' nur für die Konvertierung verwendet. Es ist nicht an das resultierende BigDecimal-Objekt gebunden. –

+0

Das bedeutet, dass die Genauigkeitseinschränkung nicht auf Operationen angewendet wird, die auf den mit der Factory erstellten Instanzen von 'BigDecimal' ausgeführt werden, es sei denn, Sie stellen den 'MathContext' als Argument für relevante Operationen bereit. –

+0

Argh! Du hast recht. Ich habe deine Kommentare in einem der anderen Threads in diesem Post erwähnt, damit die Leute davon erfahren. –

2

Gibt es eine Möglichkeit, eine ‚globale Genauigkeit‘ für alle BigDecimal Berechnungen zu setzen?

Nr

Sie erhalten eine Wrapper-Klasse erstellen müssen, die eine MathContext als zusätzliches Attribut hat. Es wird benötigt:

  • Verwendung dieses mc für jede mathematische Operation, die ansonsten die Standardsemantik verwenden würde, und

  • erstellen und zurück jedes Mal eine andere umhüllte Instanz der umhüllte Betrieb eine regelmäßige Instanz zurückgibt.

(Als Variante könnte man eine ‚globale‘ MathContext mit einem statischen implementieren, aber Sie müssen noch wrappering verwenden, um sicherzustellen, dass die mc verwendet wird.)

(Erweiterung BigDecimal würde . arbeiten, und das ist diskutierbar ordentlicheres als ein Wrapper-Klasse)


Sie sagte dies in einem Kommentar:

Ich möchte wirklich nicht mein eigenes Dezimal-Modul schreiben, ich möchte nur verstehen, warum BigDecimal so unkooperativ ist.

(Design-Fragen können nur endgültig durch das Design-Team beantwortet. Jedoch ...)

Wie bei allen komplizierten Utility-Klassen, ist das Design von BigDecimal ein Kompromiss, der die erfüllen ist, Anforderungen einer breiten Palette von Anwendungsfällen. Es ist auch ein Kompromiss zwischen den konkurrierenden Meta-Anforderungen (falsches Wort) von "Mächtigkeit" und "Einfachheit".

Was Sie haben, ist ein Anwendungsfall, der nicht besonders gut unterstützt wird. Aber ich vermute, dass, wenn es gut unterstützt wurde (z. B. mit einem globalen MathContext alles oder eine MathContext an jedem BigDecimal angeschlossenen) dann würde das alle Arten von anderen Komplexitäten einführen; z.B. Umgang mit Operationen, bei denen zwei oder mehr konkurrierende Kontextobjekte zu berücksichtigen sind. Solche Probleme könnten angegangen werden ... aber sie können zu "Überraschungen" für den Programmierer führen, und das ist keine gute Sache.

Der aktuelle Ansatz ist einfach und leicht zu verstehen, und wenn Sie etwas komplizierteres benötigen, können Sie es implementieren ... indem Sie explizit einen MathContext für die Operationen, die es erfordern.