Scala和Java中的RoundingMode.HALF_UP差异

时间:2017-02-22 16:03:24

标签: java scala rounding bigdecimal

在scala中,我尝试使用HALF_UP舍入模式将以下数字四舍五入到3位小数。 8409.3555

不幸的是,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();
}

有任何已知问题吗?

2 个答案:

答案 0 :(得分:2)

通常,您不希望直接将文字双精度转换为BigDecimal,因为您可能会被浮点舍入错误所困扰。 scaladoc中还有一个警告:

  

从Double或Float创建BigDecimal时,必须小心,因为Double和Float的二进制分数表示不容易转换为十进制表示

我们可以看到java.math.BigDecimal

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

当您尝试将该数字(一半)向上舍入时,现在为8409.355。 scala.math.BigDecimal.apply使用MathContext立即对传递给Double的{​​{1}}进行舍入,以便生成的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这样做。

答案 1 :(得分: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

我注意到的第一件事是Scala使用的不同于MathContext而不是Java。

我尝试在两者中使用相同的MathContext.UNLIMITED,但它没有改变任何内容。

然后我注意到在scala中BigDecimal java的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被转换为string。

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