2017-02-22 8 views
3

В scala я пытаюсь округлить следующее число до трехзначной точности с использованием режима округления HALF_UP. 8409,3555Разница в RoundingMode.HALF_UP в scala и Java

К сожалению 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'? Оба раза в Скала? Вы пробовали называть Java 'round' от Scala и наоборот? –

+0

Я генерирую как двойной «Double d = 8409.3555;» –

ответ

2

В общем, вы не хотите, чтобы конвертировать буквальные числа в BigDecimal напрямую, потому что вы можете быть укушены ошибками округления с плавающей точкой. Существует также предупреждение в scaladoc:

При создании BigDecimal из Double или Float, необходимо соблюдать осторожность, как дробное представление 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 передается apply немедленно, так что в результате BigDecimal имеет увеличенный шанс иметь такое же значение, как буквальный Double вы прошли в течение первого фрагмента, что действительно называют это:.

Было бы лучше представить эти литералы в виде строк, чтобы избежать ошибок округления при хранении 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 

Первое, что я заметил, что Scala использует diffrent MathContext чем Java.

Я попытался использовать тот же MathContext.UNLIMITED в обоих, но ничего не изменил.

Потом я заметил, что в BigDecimal java`s SCALA в 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).

Итак, в основном причина, по которой вы получаете отличный результат, состоит в том, что double преобразуется в строку в конструкторе scala.

Возможно, вы могли бы использовать BigDecimal java в вашем случае (как в roundWithJava)?

+1

Я фактически переписываю код ETL на основе JAVA для ETL, основанного на scala/spark. Когда я сравнивал старый результат процесса с новым процессом, я заметил эту ошибку округления. Я очень хотел использовать библиотеку scala, но теперь я использую библиотеку Java, чтобы убедиться, что результаты согласованы. благодаря –

Смежные вопросы