Scala 除以零会产生不同的结果

Scala division by zero yields different results

我对 Scala 如何处理被零除感到困惑。这是一个 REPL 代码片段。

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

如您在上面的示例中所见,根据除以零的方式,您会得到以下结果之一:

这使得调试非常具有挑战性,尤其是在处理未知特征的数据时。这种方法背后的原因是什么,或者更好的问题是,如何在 Scala 中以统一的方式处理被零除?

这一切都归结为各种类型的除零规则。

0 / 0 整数除以零 (因为两个参数都是整数文字),并且需要抛出 java.lang.ArithmeticException.

1.toDouble/0.toDouble 浮点数除以零 且分子为正数,需要计算为 +Infinity.

0.0/0.0 是一个分子为零的浮点数除法,计算结果为 +NaN.

是必需的

第一个是 Java 和 Scala 约定,另外两个是 IEEE754 浮点的属性,Java 和 Scala 都使用。

DoublesFloatsfloating-point 值(more here) which can be represented as +Infinity -Infinity and NaN as defined in IEEE 754 标准。
Integersfixed numbers,它们无法明确指示无效数据,因此它们会抛出 exceptions
对此的统一解决方案是在 Try

上使用 getOrElse 方法
Try(x/y).getOrElse(0)

如果您只想在 ArithmeticException 上恢复,您可以使用 recoverget

Try(x/y).recover{ case _: ArithmeticException => 0 }.get

recover 允许您将 Failure 转换为 Success
您也可以使用 TryOption 到 return "no result" 而不显示异常

Try(x/y).toOption

你可以使用偏函数来做这样的事情。例如:

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 

    }   
}

部分函数允许您检查是否为给定输入定义了函数。在上面的示例中,我说如果除数为 0.0,则函数 safeDiv 未定义。因此,您可以检查函数是否会在给定输入的情况下执行。不需要检查,但是 safeDiv(1.0, 0.0) 不会执行。

偏函数是你反对这样的东西的朋友:

scala> (1.0/0.0).toInt
res22: Int = 2147483647