在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();
}
有任何已知问题吗?
答案 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
中所做的那样)?