I am confused with how Scala handles division by zero. Here is a REPL code snippet.
scala> 1/0
java.lang.ArithmeticException: / by zero
... 33 elided
scala> 1.toDouble/0.toDouble
res1: Double = Infinity
scala> 0.0/0.0
res2: Double = NaN
scala> 0/0
java.lang.ArithmeticException: / by zero
... 33 elided
scala> 1.toInt/0.toInt
java.lang.ArithmeticException: / by zero
... 33 elided
As you can see in the above example, depending on how you divide by zero, you get one of the following:
This makes debugging quite challenging especially when dealing with data of unknown characteristics. What is the reasoning behind this approach, or even a better question, how to handle division by zero in a unified manner in Scala?
Floating-point expressions with division by zero produce one of three special values: Infinity , -Infinity , and NaN (“Not a Number”), as follows: If the dividend is nonzero and it has the same sign as the zero divisor (floating-point arithmetic distinguishes positive and negative zero), you get Infinity .
Java will not throw an exception if you divide by float zero. It will detect a run-time error only if you divide by integer zero not double zero. If you divide by 0.0, the result will be INFINITY.
Values like INFINITY and NaN are available for floating-point numbers but not for integers. As a result, dividing an integer by zero will result in an exception. However, for a float or double, Java allows the operation.
It's all down to the division by zero rules for various types.
0 / 0
is an integer division by zero (as both arguments are integer literals), and that is required to throw a java.lang.ArithmeticException
.
1.toDouble/0.toDouble
is a floating point division by zero with a positive numerator, and that is required to evaluate to +Infinity
.
0.0/0.0
is a floating point division by zero with a zero numerator, and that is required to evaluate to +NaN
.
The first is a Java and Scala convention, the other two are properties of IEEE754 floating point, which is what Java and Scala both use.
Doubles
and Floats
are floating-point
values (more here) which can be represented as +Infinity
-Infinity
and NaN
as defined in IEEE 754 standard.
Integers
are fixed numbers
which do not have any way of explicitly indicating invalid data, thus they throw exceptions
Unified solution to this would be to use getOrElse
method on Try
Try(x/y).getOrElse(0)
In case you want to recover only on ArithmeticException
you can use recover
and get
Try(x/y).recover{ case _: ArithmeticException => 0 }.get
recover
allows you to convert Failure
to Success
You can also use Try
to Option
to return "no result" without showing exception
Try(x/y).toOption
You can use partial functions for something like this. For example:
object MyObject {
def main(args: Array[String]) {
println(safeDiv.isDefinedAt(1.0, 1.0)) // true
println(safeDiv.isDefinedAt(1.0, 0.0)) // false
println(safeDiv(1.0, 1.0)) // 1.0
println(safeDiv(1.0, 0.0)) // crash
}
def safeDiv: PartialFunction[(Double, Double), Double] = {
case(a,b) if b != 0.0 => a/b
}
}
Partial functions allow you to check whether the function is defined for a given input. In the example above, I said the the function safeDiv
is not defined if the divisor is 0.0. Therefore you can check if the function will execute given the input. The check is not necessary, however safeDiv(1.0, 0.0)
will not execute.
Partial functions is your friend against something like this:
scala> (1.0/0.0).toInt
res22: Int = 2147483647
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With