2012-04-08 20 views
9

Il mio programma Java è incentrato su calcoli di alta precisione, che devono essere precisi con almeno 120 posizioni decimali.
Conseguentemente, tutti i numeri non interi saranno rappresentati da BigDecimals nel programma.Imposta tutte le operazioni BigDecimal con una certa precisione?

Ovviamente ho bisogno di specificare l'accuratezza del arrotondamento per le BigDecimals, al fine di evitare espressioni infinite decimali ecc
Attualmente, lo trovo un fastidio enorme di dover specificare l'accuratezza ad ogni istanziazione o operazione matematica di un BigDecimal .

C'è un modo per impostare una 'precisione globale' per tutti i calcoli BigDecimal?
(come il Context.prec() per il modulo decimale in python)

Grazie


Spec:
Java jre7 SE
di Windows 7 (32)

risposta

8

(quasi) Original

non è così semplice, ma è possibile creare un MathContext e passarlo a tutti i vostri BigDecimal costruttori ei metodi di esecuzione delle operazioni.

Rivisto

In alternativa, è possibile estendere BigDecimal e sovrascrivere eventuali operazioni che si desidera utilizzare, fornendo il diritto MathContext, e utilizzando la versione arrotondamento di 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

Questo è esattamente quello che voglio evitare. –

+0

In realtà sto riscontrando qualche difficoltà nell'utilizzo dell'estensione di classe. Se ho 2 BigDecimals che rappresentano "1" e "3", entrambi hanno scala impostata su 120 ... Divisione "1" per "3" produce ancora un decimale non terminante, anche se assegno il risultato su una scala di 120 pure! (che era quello che cercavo di evitare in origine, parametri extra per le operazioni matematiche). –

+0

Penso che sia possibile risolvere il problema sovrascrivendo il metodo 'divide' per la classe. – trutheality

-3

È possibile utilizzare il Funzione BigDecimal setScale!

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

Avrei comunque dovuto eseguire questa operazione ad ogni istanza di un BigDecimal. –

2

si potrebbe creare un classe che estende BigDecimal e imposta automaticamente la precisione per te. Allora ti limiti a usare quella lezione.

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

Questo funziona solo se il wrapper include anche tutte le operazioni BigDecimal che creano una nuova istanza. –

+2

Non funzionerà. Da JavaDocs su 'setScale': "Si noti che poiché gli oggetti BigDecimal sono immutabili, le chiamate di questo metodo non comportano la modifica dell'oggetto originale, contrariamente alla consueta convenzione di avere metodi denominati setX mutate campo X. Invece, setScale restituisce un oggetto con la scala corretta, l'oggetto restituito può o non può essere nuovamente assegnato. " Quindi chiamarlo nel costruttore non farà nulla, almeno in alcune situazioni. – SatA

0

È possibile creare un wrapper per BigDecimals, che farà questo lavoro:

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 questo caso non c'è bisogno di ignorare tutte le operazioni di BigDecimal (ad estendere caso), solo quelli usate. Ti dà il controllo su tutte le istanze e non impone di seguire il contratto BigDecimal.

3

Creare una classe BigDecimalFactory con metodi factory statici corrispondenti a tutti i costruttori che accettano MathContext - tranne che l'istanza MathContext è all'interno della fabbrica e staticamente inizializzato al momento dell'avvio. Ecco un frammento:

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

Questo non funzionerà ... se leggo correttamente javadoc. Il motivo principale è che la funzione di costruzione che stai utilizzando utilizza 'MathContext' solo per la conversione. Non è collegato all'oggetto 'BigDecimal' risultante. –

+0

Ciò significa che il vincolo di precisione non si applica alle operazioni eseguite sulle istanze 'BigDecimal' create con il factory ... a meno che non si fornisca il' MathContext' come argomento per l'operazione pertinente. –

+0

Argh! Hai ragione. Ho menzionato i tuoi commenti in uno degli altri thread in questo post in modo che la gente lo sappia. –

2

C'è un modo per impostare un 'accuratezza globale' per tutti i calcoli BigDecimal?

No.

Dovrete creare una classe wrapper che ha una MathContext come attributo in più. Sarà necessario:

  • uso questo mc per ogni operazione matematica che altrimenti utilizzare la semantica di default, e

  • creare e restituire un altro caso avvolto ogni volta che l'operazione avvolto restituisce un'istanza regolare.

(Come variante, è possibile implementare un 'globale' MathContext utilizzando una statica, ma avrai ancora bisogno di usare wrappering per garantire che il mc viene utilizzato.)

(estensione BigDecimal sarebbe . lavoro troppo, e che è più ordinato sostenibile di una classe wrapper)


Hai detto che in un commento:

Non voglio davvero scrivere il mio modulo Decimal, voglio solo capire perché BigDecimal è così poco collaborativo.

(domande di design può rispondere solo definitivamente dal team di progettazione. Tuttavia ...)

Come per tutte le classi di utilità complicate, la progettazione di BigDecimal è un compromesso che è stato progettato per soddisfare le requisiti di una vasta gamma di casi d'uso. È anche un compromesso tra le meta-esigenze in competizione (parola sbagliata) di "potenza" e "semplicità".

Quello che hai è un caso d'uso che non è particolarmente ben supportato. Ma sospetto che se fosse ben supportato (ad esempio con un globale MathContext che controlla tutto o uno MathContext collegato a ciascun BigDecimal), ciò introdurrebbe ogni sorta di altre complessità; per esempio. occuparsi di operazioni in cui sono presenti due o più oggetti contestuali concorrenti da considerare. Tali problemi potrebbero essere affrontati ... ma sono suscettibili di portare a "sorprese" per il programmatore, e questa non è una buona cosa.

L'approccio attuale è semplice e facile da capire e, se hai bisogno di qualcosa di più complicato, puoi implementarlo ... fornendo esplicitamente un MathContext per le operazioni che lo richiedono.

Problemi correlati