2010-04-23 6 views
21

Voglio passare da Java a un linguaggio di scripting per i moduli basati su Math nella mia app. Ciò è dovuto alla leggibilità e ai limiti funzionali di Java math.Scala e Java BigDecimal

Per es, in Java ho questo:

BigDecimal x = new BigDecimal("1.1"); 
BigDecimal y = new BigDecimal("1.1"); 
BigDecimal z = x.multiply(y.exp(new BigDecimal("2")); 

Come si può vedere, senza BigDecimal overloading degli operatori, formule semplici si complicano reale veloce.

Con il doppio, questo sembra buono, ma ho bisogno della precisione.

Speravo in Scala avrei potuto fare questo:

var x = 1.1; 
var y = 0.1; 
print(x + y); 

E di default che avrei avuto un comportamento simile decimale, ahimè Scala non usa il calcolo decimale per difetto.

Poi faccio questo a Scala:

var x = BigDecimal(1.1); 
var y = BigDecimal(0.1); 
println(x + y); 

E io ancora ottenere un risultato impreciso.

C'è qualcosa che non sto facendo bene in Scala?

Forse dovrei usare Groovy per massimizzare la leggibilità (utilizza i decimali per impostazione predefinita)?

+0

Sei * veramente * sicuro che l'utilizzo di BigDecimal è la cosa giusta da fare? Che tipo di calcoli stai facendo? –

+1

Non riesco a verificare il problema con BigDecimal in Scala. Funziona come previsto almeno nella 2.8.1 – gerferra

+0

var x: BigDecimal = 11.0 dovrebbe essere esatto e x = x/10.0 dovrebbe essere felice. – ChuckCottrill

risposta

35

non so Scala, ma in Java new BigDecimal(1.1) inizializza il BigDecimal con un valore double e quindi non è esattamente pari a 1.1. In Java devi invece usare new BigDecimal("1.1"). Forse ciò aiuterà anche in Scala.

+1

Uomo, è brutto. Immagino che se ti interessa la precisione decimale, usare Groovy è di gran lunga l'opzione più carina. Il suo supporto BigDecimal non è ancora completamente perfetto, ma molto meglio di quello di qualsiasi altra lingua. – mcv

+1

@mcv Quello che sta succedendo è in 'BigDecimal (1.1)', l'espressione 1.1 è analizzata come un doppio, anche prima che 'BigDecimal' imponga il suo valore. Quindi hai bisogno di 'BigDecimal (" 1.1 ")' in modo che 'BigDecimal' possa essere responsabile di decidere cosa significa" 1.1 ". Questo è un problema generale (in qualsiasi linguaggio BigDecimal o equivalente) che AFAICT è possibile risolvere solo con la lingua che non legge automaticamente i valori letterali numerici come valori a virgola mobile (che ho sempre pensato avrebbe risparmiato un sacco di confusione), ma io ' Non sono a conoscenza di alcun linguaggio che faccia le cose in questo modo. – ShreevatsaR

+0

@ShreevatsaR Questo è esattamente ciò che Groovy fa. Ha il supporto nativo BigDecimal e interpreta immediatamente 1.1 come BigDecimal. Ciò potrebbe creare confusione per le persone abituate ai galleggianti nativi, ma nella logica aziendale, di solito è ciò che realmente si desidera. – mcv

26

modificare il codice Scala a questo:

var x = BigDecimal("1.1"); // note the double quotes 
var y = BigDecimal("0.1"); 
println(x + y); 

e funzionerà esattamente come avviene in Java.

+0

Cosa sta succedendo qui? Perché "nuovo" non è né necessario né accettato? – ripper234

+0

Si noti che 'BigDecimal (" 1.0 ")' senza 'new' è una scorciatoia per' BigDecimal.apply ("1.0") '- chiama il metodo' apply' di 'oggetto BigDecimal', l'oggetto companion per' class BigDecimal' . Il metodo 'apply' è un metodo factory. – Jesper

+0

Non funziona con 'new' (almeno non in Scala 2.10.0), perché apparentemente' BigDecimal' non ha un costruttore pubblico che accetta 'String'. Probabilmente è un bug nella versione di Scala di "BigDecimal". Si noti che funziona se si esegue 'new java.math.BigDecimal (" 1.0 ")'. – Jesper

4
scala> implicit def str2tbd(str: String) = new { 
    |  def toBD = BigDecimal(str) 
    | } 
str2tbd: (str: String)java.lang.Object{def toBD: scala.math.BigDecimal} 

scala> var x = "1.1".toBD 
x: scala.math.BigDecimal = 1.1 

scala> var y = "0.1".toBD 
y: scala.math.BigDecimal = 0.1 

scala> x + y 
res0: scala.math.BigDecimal = 1.2 

scala> implicit def str2bd(str: String) = BigDecimal(str) 
str2bd: (str: String)scala.math.BigDecimal 

scala> x + y + "1.2345566" 
res1: scala.math.BigDecimal = 2.4345566 

scala> 
+0

Questo in realtà non risponde alla domanda, ma dimostra semplicemente un modo diverso di utilizzare il costruttore di stringhe già trattato in diverse risposte! –

12

Scala è decisamente la stessa di Java in questo senso.

Come per la risposta di Joachim, scrivendo val x = BigDecimal(1.1)

è equivalente a scrivere

val d : Double = 1.1 
val x = BigDecimal(d) 

Il problema, naturalmente, è che il doppio d ha già l'errore di arrotondamento, quindi sei inizializzazione x con dati sbagliati.

Utilizzare invece il costruttore che accetta una stringa e tutto andrà bene.

Dato il tuo esempio, staresti meglio usando val s invece di var s, e puoi tranquillamente lasciare il punto e virgola anche in Scala.

+1

Non riesco a verificare il problema con BigDecimal in Scala. Funziona come previsto in 2.8.1 – gerferra

+0

Non è equivalente. Nel secondo caso, si poluta lo spazio dei nomi con una nuova variabile, 'd'. –

5

È possibile memorizzare valori come Integer/String (senza precisione) internamente e utilizzare scale (questa è una trascrizione da Scala REPL):

scala> val Scale = 2 
Scale: Int = 2 

scala> val x = BigDecimal(110, Scale) 
x: scala.math.BigDecimal = 1.10 

scala> val y = BigDecimal(303, Scale) 
y: scala.math.BigDecimal = 3.03 

scala> (x+y, (x+y).scale) 
res0: (scala.math.BigDecimal, Int) = (4.13,2) 

scala> (x*2, (x*2).scale) 
res1: (scala.math.BigDecimal, Int) = (2.20,2) 

Oppure, se si vuole analizzare una stringa, è possibile controllare l'arrotondamento :

scala> val z = BigDecimal("8.937").setScale(Scale, BigDecimal.RoundingMode.FLOOR)  
z: scala.math.BigDecimal = 8.93 

scala> val z = BigDecimal("8.937").setScale(Scale, BigDecimal.RoundingMode.CEILING) 
z: scala.math.BigDecimal = 8.94 
+1

Perché usi 'val x = new BigDecimal (new JBigD (JBigI.valueOf (110), Scale))' invece di 'val x = BigDecimal (110, Scale)'? –

+1

Grazie per la segnalazione! Non riesco a ricordare se questo metodo fosse disponibile in Scala 2.7.x, ma è sicuramente il modo di andare in Scala 2.8. Risolverò la risposta. –

2

so che questa domanda è vecchio e ha risposto, ma un'altra opzione, se siete aperti a diversi linguaggi (come il PO sembrava essere), sarebbe quella di utilizzare Clojure. Clojure ha, IMO, alcuni dei sintassi semplice per BigDecimal matematica (notare le finali M s - che indica BigDecimal):

user=> (def x 1.1M) 
#'user/x 
user=> (def y 1.1M) 
#'user/y 
user=> (def z (* x (.pow y 2))) 
#'user/z 
user=> z 
1.331M 
user=> (type z) 
java.math.BigDecimal 

mi piace Clojure per la matematica in quanto il valore predefinito è di precisione in molti casi, ad esempio, il suo uso di Ratio:

user=> (/ 60 14) 
30/7 
user=> (type (/ 60 14)) 
clojure.lang.Ratio 
+1

Definitivamente migliore della versione di Scala presentata - se si apprezza la notazione polacca. – ziggystar

+1

@ziggystar Sì, devi essere disposto ad accettare il prefisso/notazione polacca, che potrebbe essere visto come un grande ostacolo per molti :) – overthink

+0

Volevo solo sottolineare che, poiché Clojure è così malleabile, è possibile che utilizzi infisso notazione con macro. [Questo post] (http://data-sorcery.org/2010/05/14/infix-math/) mostra un esempio nella libreria Incanter di alcuni anni fa. – ShawnFumo