【问题标题】:Scala division by zero yields different resultsScala除以零产生不同的结果
【发布时间】: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 我明白你的意思。但是,我想大多数人都会同意这种方法容易出现一些非常肮脏的错误,并且在您编写的每一个算术运算中都非常繁琐。

标签: java scala arithmetic-expressions divide-by-zero


【解决方案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
    

    【讨论】:

    • 太棒了。我认为它会变得很方便
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 2018-05-23
    • 2017-09-11
    • 2020-03-14
    • 1970-01-01
    • 2015-10-31
    • 1970-01-01
    相关资源
    最近更新 更多