简体   繁体   English

Scala和Java中的RoundingMode.HALF_UP差异

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM