Spray-json JsNumber BigDecimal isValid* странность

Может кто-нибудь объяснить мне это:

scala> import spray.json._

import spray.json._



scala> import DefaultJsonProtocol._

import DefaultJsonProtocol._


scala> def check(n: BigDecimal) = {

     |     println(s"n.isValidByte = ${n.isValidByte}")

     |     println(s"n.isValidChar = ${n.isValidChar}")

     |     println(s"n.isValidDouble = ${n.isValidDouble}")

     |     println(s"n.isValidFloat = ${n.isValidFloat}")

     |     println(s"n.isValidInt = ${n.isValidInt}")

     |     println(s"n.isValidLong = ${n.isValidLong}")

     |     println(s"n.isValidShort = ${n.isValidShort}")

     | }

check: (n: BigDecimal)Unit

scala> check(JsNumber(0.16) match { case JsNumber(x) => x})

n.isValidByte = false

n.isValidChar = false

n.isValidDouble = false

n.isValidFloat = false

n.isValidInt = false

n.isValidLong = false

n.isValidShort = false

Я использую спрей-json 1.2.6 со Scala 2.10.3, и это мои зависимости от библиотеки:

"io.spray" %% "spray-json" % "1.2.6"

Спасибо, Грега.


person Grega Kešpret    schedule 12.06.2014    source источник


Ответы (1)


Проблема в том, что преобразование из BigDecimal в число с плавающей запятой не идеально, поэтому оно возвращает false. floatValue должно округляться. Наблюдать:

BigDecimal(.16).floatValue                      //> res8: Float = 0.16

Но когда вы пытаетесь преобразовать BigDecimal -> Float -> BigDecimal, вы видите ошибку округления:

BigDecimal(BigDecimal(.16).floatValue)          //> res8: scala.math.BigDecimal = 0.1599999964237213

Если вы выбрали другое значение, которое может быть точно выражено в степени двойки, тогда ваша функция проверки будет работать:

  BigDecimal(BigDecimal(.5).floatValue)           //> res10: scala.math.BigDecimal = 0.5
  BigDecimal(.5).isValidFloat                     //> res11: Boolean = true

TLDR BigDecimal хорошо справляется с произвольными десятичными знаками, число с плавающей запятой хранится в степени двойки, поэтому вместо 0,16 лучшее, что он может сделать, это получить очень близкое значение 0,1599999964237213, но поскольку есть некоторые потери, isValidFloat возвращает false.

person Gangstead    schedule 12.06.2014