Scala et Java BigDecimal
je veux passer de Java à un langage de script pour les modules mathématiques de mon application. Cela est dû à la lisibilité, et les limitations fonctionnelles de mathy Java.
Pour l'e.g, en Java, j'ai ceci:
BigDecimal x = new BigDecimal("1.1");
BigDecimal y = new BigDecimal("1.1");
BigDecimal z = x.multiply(y.exp(new BigDecimal("2"));
comme vous pouvez le voir, sans surcharge de l'opérateur BigDecimal, les formules simples se compliquent très rapidement.
avec les doubles, ça a l'air bien, mais j'ai besoin de précision.
j'espérais Qu'à Scala je pourrais faire ceci:
var x = 1.1;
var y = 0.1;
print(x + y);
et par défaut j'obtiendrais un comportement de type décimal, hélas Scala n'utilise pas le calcul décimal par défaut.
Puis-je faire cela en Scala:
var x = BigDecimal(1.1);
var y = BigDecimal(0.1);
println(x + y);
Et j'obtiens toujours un résultat imprécis.
y a-t-il quelque chose que je ne fais pas bien à Scala?
peut-être que je devrais utiliser Groovy pour maximiser la lisibilité (il utilise des décimales par défaut)?
6 réponses
je ne sais pas Scala, mais en Java new BigDecimal(1.1)
initialise le BigDecimal
avec un double
valeur et donc il n'est pas exactement égale à 1.1. En Java, vous devez utiliser new BigDecimal("1.1")
à la place. Peut-être que ça aidera aussi à Scala.
changez votre code Scala en ceci:
var x = BigDecimal("1.1"); // note the double quotes
var y = BigDecimal("0.1");
println(x + y);
et il va travailler comme il le fait en Java.
Scala est certainement le même que Java à cet égard.
selon la réponse de Joachim, écrire val x = BigDecimal(1.1)
est équivalent à l'écriture
val d : Double = 1.1
val x = BigDecimal(d)
Le problème, bien sûr, est que le Double d
a déjà l'erreur d'arrondi, donc vous initialisez x avec de mauvaises données.
utilisez le constructeur qui accepte une chaîne à la place, et tout ira bien.
Donné à votre exemple, vous seriez mieux d'utiliser val
s au lieu de var
s, et vous pouvez laisser le point-virgule éteint en toute sécurité à Scala aussi bien.
vous pouvez stocker des valeurs comme entier/chaîne (sans précision) en interne et utiliser scale
(c'est une transcription de la 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)
Ou si vous souhaitez analyser une chaîne de caractères, vous pouvez contrôler l'arrondissement:
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
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>
je sais que cette question Est ancienne et qu'on y répond, mais une autre option, si vous êtes ouvert à différentes langues (comme L'OP semblait être), serait D'utiliser Clojure. Clojure a, IMO, une syntaxe simple pour BigDecimal
math (notez le résultat M
s -- qui indique 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
J'aime Clojure pour les maths car il est par défaut précis dans de nombreux cas, par exemple son utilisation de Ratio
:
user=> (/ 60 14)
30/7
user=> (type (/ 60 14))
clojure.lang.Ratio