[英]RoundingMode.HALF_UP difference in scala and Java
In scala, I am trying to round up the following number to the 3 decimal precision using HALF_UP rounding mode. 在scala中,我试图使用HALF_UP舍入模式将以下数字四舍五入到3位小数。 8409.3555
8409.3555
Unfortunately scala is returning 8409.356, but Java is returning 8409.355 不幸的是,scala返回8409.356,但Java返回8409.355
Scala code: Scala代码:
def round(d: Double): Double = {
BigDecimal.apply(d).setScale(3, RoundingMode.HALF_UP).doubleValue()
}
Java code: Java代码:
private static Double round(Double d) {
BigDecimal bd = new BigDecimal(d);
bd = bd.setScale(3, RoundingMode.HALF_UP);
return bd.doubleValue();
}
Is there any known issue? 有没有已知的问题?
In general, you don't want to convert literal doubles to BigDecimal
directly, because you may be bitten by floating point rounding errors. 通常,您不希望直接将文字双精度转换为
BigDecimal
,因为您可能会被浮点舍入错误所困扰。 There is also a warning in the scaladoc : scaladoc中还有一个警告:
When creating a BigDecimal from a Double or Float, care must be taken as the binary fraction representation of Double and Float does not easily convert into a decimal representation
从Double或Float创建BigDecimal时,必须注意Double和Float的二进制分数表示不容易转换为十进制表示
We can see it happen with java.math.BigDecimal
: 我们可以看到它发生在
java.math.BigDecimal
:
scala> new java.math.BigDecimal(8409.3555)
res1: java.math.BigDecimal = 8409.355499999999665305949747562408447265625
When you try to round that number (half) up, it is now 8409.355. 当你试图将这个数字(一半)向上舍入时,它现在是8409.355。
scala.math.BigDecimal.apply
uses a MathContext
to round the Double
passed to apply
immediately, so that the resulting BigDecimal
has an increased chance of having the same value as the literal Double
you passed in. Within the first snippet, what's really being called is: 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
It would be better to represent these literals as strings to avoid any rounding errors from storing the Double
. 最好将这些文字表示为字符串,以避免存储
Double
任何舍入错误。 For example: 例如:
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
If you must convert from Double
, I'd suggest using scala.math.BigDecimal.apply
to do so. 如果你必须从
Double
转换,我建议使用scala.math.BigDecimal.apply
来实现。
I could also reproduce this issue. 我也可以重现这个问题。
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
First thing I noticed is that Scala is using diffrent MathContext
than Java. 我注意到的第一件事是Scala使用的是与Java不同的
MathContext
。
I tried using same MathContext.UNLIMITED
in both, but it didn't changed anything. 我尝试在两者中使用相同的
MathContext.UNLIMITED
,但它没有改变任何东西。
Then I noticed that in scala's BigDecimal
java`s BigDecimal is constructed like this: 然后我注意到在scala的
BigDecimal
java的BigDecimal构造如下:
new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc)
I tried using it in Java: 我尝试在Java中使用它:
val bd = new java.math.BigDecimal(java.lang.Double.toString(d), java.math.MathContext.UNLIMITED).setScale(3, java.math.RoundingMode.HALF_UP)
and I received same result ( 8409.356
). 我收到了同样的结果(
8409.356
)。
So basically the reason why you get diffrent result's is that double is transformed to string in scala's constructor. 所以基本上你得到不同结果的原因是在scala的构造函数中double被转换为字符串。
Maybe you could use java's BigDecimal in your case (as I did in roundWithJava
)? 也许你可以在你的情况下使用java的BigDecimal(就像我在
roundWithJava
所做的roundWithJava
)?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.