2017-02-22 27 views
3

在scala中,我试图用HALF_UP舍入模式将下面的数字四舍五入到3位小数精度。 8409.3555Scala和Java中的RoundingMode.HALF_UP区别

不幸的是Scala是返回8409.356,但Java将返回8409.355

Scala代码:

def round(d: Double): Double = { 
    BigDecimal.apply(d).setScale(3, RoundingMode.HALF_UP).doubleValue() 
} 

Java代码:

private static Double round(Double d) { 
    BigDecimal bd = new BigDecimal(d); 
    bd = bd.setScale(3, RoundingMode.HALF_UP); 
    return bd.doubleValue(); 
} 

是否有任何已知问题?

+0

你如何生成'8409.3555'来传递给'round'?两次在斯卡拉?你有没有试过从Scala调用Java'round',反之亦然? –

+0

我生成双“双d = 8409.3555;” –

回答

2

通常,您不希望直接将文字双打转换为BigDecimal,因为您可能会被浮点舍入误差咬伤。还有在scaladoc警告:

当从一个双人间或浮动创建一个BigDecimal,必须小心为Double的二进制小数表示和浮动不容易转换成十进制表示

我们可以看到它与java.math.BigDecimal发生:

scala> new java.math.BigDecimal(8409.3555) 
res1: java.math.BigDecimal = 8409.355499999999665305949747562408447265625 

当你试图把这个数字(半)了,现在是8409.355。 scala.math.BigDecimal.apply使用MathContext圆的Double传递给apply立即,使得所得到的BigDecimal具有相同的值,你传递的字面Double的一个增加的机会在第一个片段,有什么真正被称为是:

scala> scala.math.BigDecimal(8409.3555)(java.math.MathContext.DECIMAL128) 
res10: scala.math.BigDecimal = 8409.3555 

将这些文字表示为字符串以避免存储Double时发生任何舍入错误会更好。例如:

scala> scala.math.BigDecimal("8409.3555") 
res17: scala.math.BigDecimal = 8409.3555 

scala> new java.math.BigDecimal("8409.3555") 
res18: java.math.BigDecimal = 8409.3555 

如果必须Double转换,我建议使用scala.math.BigDecimal.apply这样做。

0

我也可以重现此问题。

def roundWithScala(d: Double): Double = { 
    BigDecimal(d).setScale(3, RoundingMode.HALF_UP).doubleValue() 
} 

def roundWithJava(d: Double): Double = { 
    val bd = new java.math.BigDecimal(d).setScale(3, java.math.RoundingMode.HALF_UP) 
    return bd.doubleValue() 
} 

println(roundWithScala(8407.3555)) //8407.356 
println(roundWithJava(8407.3555)) //8407.355 

println(roundWithScala(8409.3555)) //8409.356 
println(roundWithJava(8409.3555)) //8409.355 

println(roundWithScala(8409.4555)) //8409.456 
println(roundWithJava(8409.4555)) //8409.456 

我注意到的第一件事是,斯卡拉使用比Java更差MathContext

我试过在两者中都使用相同的MathContext.UNLIMITED,但它没有改变任何东西。

然后我注意到,在Scala的BigDecimal java`s BigDecimal的构造是这样的:

new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc) 

我试过在Java中使用它:

val bd = new java.math.BigDecimal(java.lang.Double.toString(d), java.math.MathContext.UNLIMITED).setScale(3, java.math.RoundingMode.HALF_UP) 

,我收到相同的结果(8409.356)。

所以基本上你得到不同的结果的原因是,在scala的构造函数中double被转换为字符串。

也许你可以在你的情况下使用java的BigDecimal(就像我在roundWithJava中做的那样)?

+1

我实际上是将基于JAVA的ETL代码重写为基于scala/spark的ETL。当我将旧的过程结果与新过程进行比较时,我注意到了这个舍入错误。我热衷于使用scala库,但现在我正在使用Java库来确保结果一致。谢谢 –

相关问题