Scala Int溢出

时间:2018-04-23 05:57:45

标签: scala

任何人都可以帮助我理解为什么Int类型溢出结果与更高的数字不一致。有时它是正的,有时是负的,最后收敛到零。

import scala.annotation.tailrec

object FactorialExample {

  def main(args: Array[String]): Unit = {
    (1 to 100).foreach(i => println(s"Factorial of ${i} is " + factorial(i)))
  }

  def factorial(i: Int): Int = {
    @tailrec
    def fact(i: Int, acc: Int): Int = {
      if (i <= 0) acc
      else fact(i - 1, acc * i)
    }
    fact(i, 1)
  }
}

2 个答案:

答案 0 :(得分:6)

正如其他人所指出的, Scala Java ,默认情况下不对整数值执行溢出检查。 (我同意,静默溢出一个值可能会导致意外和不需要的行为,以及许多非常微妙的错误。)然而,一切都不会丢失。 java.lang.Math具有静态方法(addExactincrementExactmultiplyExactsubtractExact等),如果计算溢出,则会抛出ArithmeticException基础类型。例如:

scala> def factorial(n: Int): Int = {
     |   if(n <= 1) 1
     |   else Math.multiplyExact(n, factorial(n - 1))
     | }
factorial: (n: Int)Int

scala> factorial(12)
res0: Int = 479001600

scala> factorial(13)
java.lang.ArithmeticException: integer overflow
  at java.lang.Math.multiplyExact(Math.java:867)
  at .factorial(<console>:13)
  ... 28 elided

此版本不是尾递归,抛出异常违反函数式编程原则(更好的方法是返回封装可能错误的类型,例如Try[Int]或{{ 1}}),但它会检测并报告Either[Throwable, Int]类型的溢出。有关详细信息,请参阅java.lang.Math

例如,这是一个不会耗尽堆栈的功能版本:

Int

关于溢出值:您可以将此类结果视为有效随机。它是仅保留溢出结果的最右边32位(scala> import scala.annotation.tailrec import scala.annotation.tailrec scala> import scala.util.Try import scala.util.Try scala> def factorial(n: Int): Try[Int] = { | @tailrec | def fact(i: Int, acc: Int): Int = { | if(i <= 1) acc | else fact(i - 1, Math.multiplyExact(i, acc)) | } | Try(fact(n, 1)) | } scala> factorial(12) res0: scala.util.Try[Int] = Success(479001600) scala> factorial(13) res1: scala.util.Try[Int] = Failure(java.lang.ArithmeticException: integer overflow) 类型的容量)的结果。如果设置了最高位,则该值将被视为负值。 (正如其他人指出的那样,请参阅Two's Complement以获得有关如何解释Int位模式的更详细说明。)虽然这取决于您正在执行的计算,但结果不会在一般情况下,将收敛为零。

例如,Int是导致factorial(13)溢出的最低参数值。在不使用Int的情况下,您的原始函数会返回值Math.multiplyExact - 遗憾的是 错误。如果我们使用1932053504代替Long,我们可以将此结果计算为Int。但是让我们用二进制表示来看这些数字:

6227020800

最右边的32位具有相同的值! (请注意,前导零被省略。)但是,scala> 1932053504.toBinaryString res0: String = 1110011001010001100110000000000 scala> 6227020800L.toBinaryString res1: String = 101110011001010001100110000000000 表示有一个加法位,不能存储在Long表示中,这就完全不同了。不幸的是,递归算法(Int)意味着一旦我们得到溢出就会使这些错误复杂化,这意味着,对于高于13的参数值,我们开始乘以溢出值,导致总垃圾,并进一步破坏任何模式在溢出的价值。特别是,一旦将阶乘计算为零,所有后续值也将明显为零。

答案 1 :(得分:2)

  

为什么Int类型溢出结果与更高的数字不一致。有时它是正的,有时是负的,最后收敛到零。

这就是int类型溢出的行为方式。你可以在例如Modular arithmetic Two's Complement。你有特定的问题吗?

  

我明白它的溢出情况但我的问题是一旦溢出情况进入图片编译器应抛出一个数字,用户应该知道它的垃圾并需要使用其他数据类型

这是在Why, In Java arithmetic, overflow or underflow will never throw an Exception?

处回答的