Skip to content

Commit f7539ad

Browse files
committed
#37 Changed the JNumber conversion methods to follow semantics of types
1 parent 302f1dc commit f7539ad

File tree

11 files changed

+77
-50
lines changed

11 files changed

+77
-50
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,28 @@ in an undefined manner.
9191
Do note that according to the JSON spec, whether to order keys for a `JObject` is not specified. Also note that `Map`
9292
disregards ordering for equality, however `Array`/`js.Array` equality takes ordering into account.
9393

94+
## Number conversions
95+
ScalaJSON `JNumber` provides conversions to various number types with the following conventions
96+
97+
* `toInt`: Safe conversion to `Int` which accounts for values such as `1.0` and `100.00e-2` (which both evaluate to `1`).
98+
Also safely detects over/underflow.
99+
* `toLong`: Safe conversion to `Long` which accounts for values such as `1.0` and `100.00e-2` (which both evaluate to `1`).
100+
Also safely detects over/underflow.
101+
* `toDouble`: Converts to a `Double` assuming the same semantics of `Double` (i.e. precision loss is expected).
102+
* `toFloat`: Converts to a `Float` assuming the same semantics of `Float` (i.e. precision loss is expected).
103+
* `toBigInt`: Converts to a `BigInt` which accounts for values such as `1.0` and `100.00e-2` (which evaluates to `1`).
104+
Can construct a `BigInt`for as much as memory as the system has (if your system runs out of memory this is considered
105+
undefined behaviour).
106+
* `toBigDecimal`: Converts to a `BigDecimal` with all of the caveats of `BigDecimal` construction. The `BigDecimal` is
107+
constructed with `MathContext.UNLIMITED` precision.
108+
109+
With the `.toFloat` and `.toDouble` methods, if you don't want any loss in precision, its advisable to convert to
110+
`BigDecimal` first and then work from there, i.e. when working with `Decimal`/`Float`, its implied that you will
111+
have loss of precision.
112+
113+
Remember that in all cases if these methods are not applicable, you can always use the `.value` field to get the
114+
original string representation of the number.
115+
94116
## Scala.js
95117
ScalaJSON also provides support for [Scala.js](https://github.com/scala-js/scala-js).
96118
The usage of Scala.js mirrors the usage of Scala on the JVM however Scala.js also implements
@@ -103,6 +125,10 @@ underlying number (numbers in Javascript are represented as double precision flo
103125
You can use the `.value` method on a `scalajson.ast.JNumber`/`scalajson.ast.unsafe.JNumber` to
104126
get the raw string value as a solution to this problem.
105127

128+
Further, `toFloat` on `JNumber` (see [Number Conversions](#number-conversions) ) can have different semantics on Scala.js, depending on whether you have
129+
strict-floats enabled in your application. Please see the [Scala.js semantics page](https://www.scala-js.org/doc/semantics.html)
130+
for more information.
131+
106132
## jNumberRegex
107133
`scalajson.JNumber` uses `jNumberRegex` to validate whether a number is a valid
108134
JSON number. One can use `jNumberRegex` explicitly if you want to use the validation that

js/src/main/scala-2.10/scalajson.ast/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ final class JNumber private[ast] (val value: String) extends JValue {
145145

146146
def toLong: Option[Long] = scalajson.ast.toLong(value)
147147

148-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
148+
def toDouble: Double = scalajson.ast.toDouble(value)
149+
150+
def toFloat: Float = scalajson.ast.toFloat(value)
149151

150152
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
151153
}

js/src/main/scala-2.10/scalajson.ast/unsafe/JValue.scala

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ object JNumber {
8787
final case class JNumber(value: String) extends JValue {
8888
override def toStandard: ast.JValue =
8989
value match {
90-
case jNumberRegex(_ *) => new ast.JNumber(value)
91-
case _ => throw new NumberFormatException(value)
90+
case jNumberRegex(_*) => new ast.JNumber(value)
91+
case _ => throw new NumberFormatException(value)
9292
}
9393

9494
override def toJsAny: js.Any = value.toDouble match {
95-
case n if n.isNaN => null
95+
case n if n.isNaN => null
9696
case n if n.isInfinity => null
97-
case n => n
97+
case n => n
9898
}
9999

100100
def toInt: Option[Int] = scalajson.ast.toInt(value)
@@ -103,7 +103,9 @@ final case class JNumber(value: String) extends JValue {
103103

104104
def toLong: Option[Long] = scalajson.ast.toLong(value)
105105

106-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
106+
def toDouble: Double = scalajson.ast.toDouble(value)
107+
108+
def toFloat: Float = scalajson.ast.toFloat(value)
107109

108110
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
109111
}
@@ -235,11 +237,11 @@ final case class JObject(value: js.Array[JField] = js.Array()) extends JValue {
235237
else {
236238
result = 31 * result + elem.field.##
237239
elem.value match {
238-
case unsafe.JNull => unsafe.JNull.##
239-
case unsafe.JString(s) => s.##
240-
case unsafe.JBoolean(b) => b.##
241-
case unsafe.JNumber(i) => i.##
242-
case unsafe.JArray(a) => a.##
240+
case unsafe.JNull => unsafe.JNull.##
241+
case unsafe.JString(s) => s.##
242+
case unsafe.JBoolean(b) => b.##
243+
case unsafe.JNumber(i) => i.##
244+
case unsafe.JArray(a) => a.##
243245
case unsafe.JObject(obj) => obj.##
244246
}
245247
})
@@ -307,11 +309,11 @@ final case class JArray(value: js.Array[JValue] = js.Array()) extends JValue {
307309
result = 31 * result + (if (elem == null) 0
308310
else {
309311
elem match {
310-
case unsafe.JNull => unsafe.JNull.##
311-
case unsafe.JString(s) => s.##
312-
case unsafe.JBoolean(b) => b.##
313-
case unsafe.JNumber(i) => i.##
314-
case unsafe.JArray(a) => a.##
312+
case unsafe.JNull => unsafe.JNull.##
313+
case unsafe.JString(s) => s.##
314+
case unsafe.JBoolean(b) => b.##
315+
case unsafe.JNumber(i) => i.##
316+
case unsafe.JArray(a) => a.##
315317
case unsafe.JObject(obj) => obj.##
316318
}
317319
})

js/src/main/scala/scalajson/ast/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ final case class JNumber private[ast] (value: String) extends JValue {
128128

129129
def toLong: Option[Long] = scalajson.ast.toLong(value)
130130

131-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
131+
def toDouble: Double = scalajson.ast.toDouble(value)
132+
133+
def toFloat: Float = scalajson.ast.toFloat(value)
132134

133135
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
134136
}

js/src/main/scala/scalajson/ast/unsafe/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ final case class JNumber(value: String) extends JValue {
101101

102102
def toLong: Option[Long] = scalajson.ast.toLong(value)
103103

104-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
104+
def toDouble: Double = scalajson.ast.toDouble(value)
105+
106+
def toFloat: Float = scalajson.ast.toFloat(value)
105107

106108
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
107109
}

jvm/src/main/scala-2.10/scalajson.ast/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ final class JNumber private[ast] (val value: String) extends JValue {
132132

133133
def toLong: Option[Long] = scalajson.ast.toLong(value)
134134

135-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
135+
def toDouble: Double = scalajson.ast.toDouble(value)
136+
137+
def toFloat: Float = scalajson.ast.toFloat(value)
136138

137139
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
138140
}

jvm/src/main/scala-2.10/scalajson.ast/unsafe/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ final case class JNumber(value: String) extends JValue {
8383

8484
def toLong: Option[Long] = scalajson.ast.toLong(value)
8585

86-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
86+
def toDouble: Double = scalajson.ast.toDouble(value)
87+
88+
def toFloat: Double = scalajson.ast.toFloat(value)
8789

8890
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
8991
}

jvm/src/main/scala/scalajson/ast/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ final case class JNumber private[ast] (value: String) extends JValue {
113113

114114
def toLong: Option[Long] = scalajson.ast.toLong(value)
115115

116-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
116+
def toDouble: Double = scalajson.ast.toDouble(value)
117+
118+
def toFloat: Float = scalajson.ast.toFloat(value)
117119

118120
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
119121
}

jvm/src/main/scala/scalajson/ast/unsafe/JValue.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ final case class JNumber(value: String) extends JValue {
8383

8484
def toLong: Option[Long] = scalajson.ast.toLong(value)
8585

86-
def toDouble: Option[Double] = scalajson.ast.toDouble(value)
86+
def toDouble: Double = scalajson.ast.toDouble(value)
87+
88+
def toFloat: Float = scalajson.ast.toFloat(value)
8789

8890
def toBigDecimal: Option[BigDecimal] = scalajson.ast.toBigDecimal(value)
8991
}

shared/src/main/scala-2.10/scalajson.ast/package.scala

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -698,25 +698,18 @@ package object ast {
698698
}
699699
}
700700

701-
private[ast] def toDouble(value: String): Option[Double] = {
702-
try {
703-
val asDouble = value.toDouble
704-
if (BigDecimal(value, MathContext.UNLIMITED) == BigDecimal(
705-
asDouble,
706-
MathContext.UNLIMITED))
707-
Some(asDouble)
708-
else
709-
None
710-
} catch {
711-
case _: java.lang.NumberFormatException => None
712-
}
713-
}
701+
@inline private[ast] def toDouble(value: String): Double =
702+
value.toDouble
703+
704+
@inline private[ast] def toFloat(value: String): Float =
705+
value.toFloat
714706

715707
private[ast] def toBigDecimal(value: String): Option[BigDecimal] = {
716708
try {
717709
Some(BigDecimal(value, MathContext.UNLIMITED))
718710
} catch {
719-
case _: java.lang.NumberFormatException => None
711+
case _: NumberFormatException | _: ArithmeticException =>
712+
None
720713
}
721714
}
722715
}

shared/src/main/scala/scalajson/ast/package.scala

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -698,25 +698,17 @@ package object ast {
698698
}
699699
}
700700

701-
private[ast] def toDouble(value: String): Option[Double] = {
702-
try {
703-
val asDouble = value.toDouble
704-
if (BigDecimal(value, MathContext.UNLIMITED) == BigDecimal(
705-
asDouble,
706-
MathContext.UNLIMITED))
707-
Some(asDouble)
708-
else
709-
None
710-
} catch {
711-
case _: java.lang.NumberFormatException => None
712-
}
713-
}
701+
@inline private[ast] def toDouble(value: String): Double =
702+
value.toDouble
703+
704+
@inline private[ast] def toFloat(value: String): Float =
705+
value.toFloat
714706

715707
private[ast] def toBigDecimal(value: String): Option[BigDecimal] = {
716708
try {
717709
Some(BigDecimal(value, MathContext.UNLIMITED))
718710
} catch {
719-
case _: java.lang.NumberFormatException => None
711+
case _: NumberFormatException | _: ArithmeticException => None
720712
}
721713
}
722714
}

0 commit comments

Comments
 (0)