2010-04-23 117 views
21

我想从我的应用程序中的基于Math的模块切换到Java的脚本语言。这是由于mathy Java的可读性和功能限制。Scala和Java BigDecimal

对于e.g,在Java中我有这样的:

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

正如你所看到的,没有BigDecimal的操作符重载,简单的公式很复杂真正的快。

随着双打,这看起来不错,但我需要的精度。

我希望在斯卡拉我可以这样做:

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

,默认情况下,我会得到小数类似的行为,唉斯卡拉默认情况下不使用十进制计算。

然后,我为此在斯卡拉:

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

我仍然得到一个不精确的结果。

有没有什么我不在斯卡拉正确的做?

也许我应该使用Groovy来最大化可读性(默认使用小数)?

+0

你真的*确定使用BigDecimal是正确的事情吗?你在做什么样的计算? – 2010-04-23 10:06:43

+1

我无法验证Scala中BigDecimal的问题。至少在2.8.1中它至少能够工作 – gerferra 2011-01-20 11:23:13

+0

var x:BigDecimal = 11.0应该是确切的,并且x = x/10.0应该很高兴。 – ChuckCottrill 2016-10-14 23:52:57

回答

35

我不知道斯卡拉,但在Java new BigDecimal(1.1)double值初始化BigDecimal,因此它是不准确等于1.1。在Java中,您必须改用new BigDecimal("1.1")。也许这对斯卡拉也有帮助。

+1

男人,这是丑陋的。我猜如果你关心小数精度,那么使用Groovy是最漂亮的选择。它的BigDecimal支持还不完全完美,但远优于任何其他语言。 – mcv 2011-01-03 14:00:46

+1

@mcv发生了什么事情是在'BigDecimal(1.1)'中,表达式1.1被解析为一个double,甚至在BigDecimal获得它的值之前。所以你需要'BigDecimal(“1.1”)'这样'BigDecimal'可以负责决定“1.1”的含义。这是一个普遍的问题(在任何语言的BigDecimal或同等语言中),AFAICT只能使用不会自动将数字字面读取为浮点值的语言来解决(我一直认为这会节省大量的混淆),但是,我没有意识到任何用这种方式做事情的语言。 – ShreevatsaR 2016-09-30 18:47:21

+0

@ShreevatsaR这正是Groovy所做的。它具有本机BigDecimal支持,并立即将1.1解释为BigDecimal。对于习惯于本地浮动的人来说,这可能会让人困惑,但在商业逻辑中,这通常是您实际需要的。 – mcv 2016-10-01 23:24:22

26

更改您的Scala代码这样:

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

,它会工作就像它在Java中。

+0

这里发生了什么?为什么'new'既不需要也不被接受? – ripper234 2013-03-01 09:14:33

+0

请注意,不带'new'的BigDecimal(“1.0”)是'BigDecimal.apply(“1.0”)的缩写' - 它调用'BigDecimal'类的对象BigDecimal的'apply'方法, 。 'apply'方法是一种工厂方法。 – Jesper 2013-03-01 09:46:10

+0

它不适用于'new'(至少不在Scala 2.10.0中),因为显然'BigDecimal'没有一个带'String'的公共构造函数。这可能是Scala版本的'BigDecimal'中的一个bug。请注意,如果您执行'new java.math.BigDecimal(“1.0”)'',它将起作用。 – Jesper 2013-03-01 09:47:39

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

这实际上并没有回答这个问题,它只是演示了使用字符串构造函数的不同方法,因为已经在不同的答案中介绍过了! – 2010-05-02 17:19:52

12

在这方面,Scala与Java绝对是一样的。

按照勒夫的回答,写val x = BigDecimal(1.1)

相当于写

​​

的问题,当然,是双d已经具备了舍入误差,所以你初始化X与坏数据。

使用接受字符串的构造函数代替,并且一切正常。

考虑到您的例子,您最好使用val而不是var s,并且您可以安全地将分号保留在Scala中。

+1

我无法验证Scala中BigDecimal的问题。它在2.8.1中按预期工作 – gerferra 2011-01-20 11:21:23

+0

它不等同。在第二种情况下,你使用一个新变量'd'来对命名空间进行监控。 – 2014-07-13 18:46:38

5

您可以在内部存储值作为整数/字符串(无精),并使用scale(这是从斯卡拉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) 

或者,如果你想解析字符串,你可以控制的四舍五入:

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

为什么使用'val x = new BigDecimal(new JBigD(JBigI.valueOf(110),Scale))'而不是'val x = BigDecimal(110,Scale)'? – 2011-02-27 15:23:20

+1

谢谢指出!我不记得这种方法在Scala 2.7.x中是否可用,但它绝对是Scala 2.8中的一种方式。我会解决答案。 – 2011-02-27 18:38:13

2

我知道这个问题是旧的,并回答了,但另一种选择,如果你打开不同的语言(如OP似乎是),是使用Clojure的。 Clojure的有,海事组织,一些简单的语法为BigDecimal数学(注意尾随M秒 - 表示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 

我喜欢的Clojure的数学,因为它默认为精度在许多情况下,例如它的使用Ratio

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

明确地比提出的Scala版本更好 - 如果喜欢波兰语符号。 – ziggystar 2011-06-06 14:11:30

+1

@ziggystar是的,你必须愿意接受前缀/波兰表示法,这可以被看作是一个很大的障碍了很多:) – overthink 2011-06-06 16:11:33

+0

只是想指出的是,由于Clojure是如此延展性,可以为它使用中缀用宏表示。本帖](http://data-sorcery.org/2010/05/14/infix-math/)显示,从几年前的咒术库的例子。 – ShawnFumo 2014-03-11 01:10:37