任何人都可以帮助我理解为什么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)
}
}
答案 0 :(得分:6)
正如其他人所指出的, Scala 和 Java ,默认情况下不对整数值执行溢出检查。 (我同意,静默溢出一个值可能会导致意外和不需要的行为,以及许多非常微妙的错误。)然而,一切都不会丢失。 java.lang.Math
具有静态方法(addExact
,incrementExact
,multiplyExact
,subtractExact
等),如果计算溢出,则会抛出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?
处回答的