Scala除以零产生不同的结果

Posted

技术标签:

【中文标题】Scala除以零产生不同的结果【英文标题】:Scala division by zero yields different results 【发布时间】:2017-10-11 19:32:20 【问题描述】:

我对 Scala 如何处理除以零感到困惑。这是一个 REPL 代码 sn-p。

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

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

“java.lang.ArithmeticException: / 零” “双数 = NaN” “双 = 无穷大”

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

【问题讨论】:

我认为这与不同的数据类型有关。在执行不同但相关的计算时,请尝试始终使用相同的数据类型。例如:Double、Int 等 您可能正在处理未知特征的数据,但在像 Scala 这样的静态类型语言中,您处理的不是未知类型的数据。 @AlexeyRomanov 我明白你的意思。但是,我想大多数人都会同意这种方法容易出现一些非常肮脏的错误,并且在您编写的每一个算术运算中都非常繁琐。 【参考方案1】:

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

0 / 0 是一个 被零除的整数(因为两个参数都是整数文字),这是抛出 java.lang.ArithmeticException 所必需的。

1.toDouble/0.toDouble 是一个 浮点除以零,分子为正数,计算结果为 +Infinity 所必需的。

0.0/0.0 是用零分子除以零的浮点数,它需要计算为+NaN

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

【讨论】:

【参考方案2】:

DoublesFloatsfloating-point 值 (more here),可以表示为 +Infinity -InfinityNaN,如 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 来返回“无结果”而不显示异常

Try(x/y).toOption

【讨论】:

谢谢。您的回答非常有帮助,因为它详细说明了可能的解决方案。 我发现 Try(1.0 / 0.0) 将返回 Success(Infinity) 而不是 Failure 并且 OrElse 不会返回。【参考方案3】:

您可以将部分函数用于这样的事情。例如:

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

【讨论】:

太棒了。我认为它会变得很方便

以上是关于Scala除以零产生不同的结果的主要内容,如果未能解决你的问题,请参考以下文章

从两个不同的表计算增长百分比时除以零误差

Access VBA 函数为不同的用户产生不同的结果

一致性:将整数除以 2 的幂与 10 的幂?

JAVA里INT除以INT类型得的结果为FLOAT或者DOUBLE怎样做?

无论结果如何,支持除以零的最快整数除法是啥?

Scala-Spark 不同的结果 [关闭]