2017-02-22 29 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庫來確保結果一致。謝謝 –

相關問題