Спрей-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
Я использую Spray-JSON 1.2.6 с Scala 2.10.3, и это мои библиотеки LibraryDependencies:
"io.spray" %% "spray-json"% "1.2.6"
Спасибо Грега
1 ответ
Проблема в том, что преобразование из BigDecimal в float не является идеальным, поэтому возвращает false. FloatValue должно выполнить некоторое округление. Заметим:
BigDecimal(.16).floatValue //> res8: Float = 0.16
Но когда вы пытаетесь конвертировать BigDecimal -> Float -> BigDecimal, вы видите ошибку округления:
BigDecimal(BigDecimal(.16).floatValue) //> res8: scala.math.BigDecimal = 0.1599999964237213
Если вы выбрали другое значение, которое может быть точно выражено в степенях 2, тогда ваша функция проверки будет работать:
BigDecimal(BigDecimal(.5).floatValue) //> res10: scala.math.BigDecimal = 0.5
BigDecimal(.5).isValidFloat //> res11: Boolean = true
TLDR BigDecimal хорошо работает с произвольными десятичными числами, с плавающей запятой хранится в степенях два, поэтому вместо 0,16 лучшее, что он может сделать, - это получить очень близкое значение 0,1599999964237213, но, поскольку есть некоторая потеря, isValidFloat возвращает false.