Reputation: 41909
Learn You a Haskell shows the factorial
function:
Prelude> factorial n = product [1..n]
Prelude> factorial 50
30414093201713378043612608166064768844377641568960512000000000000
Looks like either an Int
is larger than 32-bits or Haskell has a BigNumber
-like type.
Running that "similar" function in Scala.
scala> def factorial(n: Int) = List.range(1, n+1).foldLeft(1)(_*_)
factorial: (n: Int)Int
I can calculate a factorial that stays within Int.MaxValue
(2 billion something)
scala> factorial(10)
res5: Int = 362880
Overflow occurs due to the result exceeding Int.MaxValue
scala> factorial(33)
res6: Int = -2147483648
But, why isn't it overflowing here?
scala> factorial(50)
res7: Int = 0
So, how's it work in Haskell? And, why does it result in 0 for Scala?
Upvotes: 2
Views: 2043
Reputation: 20405
In addition to aforementioned reasons on Haskell Int
and Integer
machine dependent and arbitrary precision, the equivalent in Scala for Integer
would be
def factorial(n: Int) = (BigInt(1) to BigInt(n)).product
where BigInt
provides arbitrary precision and relies on java.math.BigInteger
.
Upvotes: 7
Reputation: 3947
There are (at least) two types for whole numbers in Haskell: Integer
which can hold arbitrarily large numbers, but arithmetic is slower, and Int
, with a machine dependent maximal value and faster arithmetic (see also this question: Haskell Int and Integer)
Haskell's default integer type is Integer
, that's why there's no overflow in your haskell example. Scala's Int
(32 bit signed) however can overflow and then wraps around into negative numbers.
This is the reason for odd results as in factorial(50)=0
(and factorial(51) = factorial(52) = ... = 0
: Due to overflows, a temporary result in the multiplication is 0
, thus all following factorials are 0
as well.
Example using decimal numbers: Let's assume that we are using a fixed width decimal representation with 3 places and only positive numbers (in reality, we would have 32 or 64 binary digits and Haskell and Scala both use signed numbers, but the basic concept stays the same):
An example on my 64 bit box, using Haskell:
Prelude> factorial 65 :: Int # largest nonzero factorial on my box
-9223372036854775808
Prelude> factorial 66 :: Int # all following factorials are zero
0
Prelude> -9223372036854775808 * 66 :: Int # because (factorial 65) * 66 = 0
0
Prelude> -9223372036854775808 * 66 # with arbitrary precision:
-608742554432415203328
Prelude> -608742554432415203328 / 2^64 # this happens to be a multiple of 2^64
-33.0
The same thing would happen with factorials in a fixed length base 10 representation: each factor that is a multiple of 10 introduces another 0 at the "right end of the number", so eventually, all digits of our imaginary fixed length base 10 integer are shifted out to the left.
With the internal binary representation used in Haskells or Scalas Int
, the same happens for each even factor, so that at some point, all bits are 0.
Upvotes: 7
Reputation: 48644
According to the documentation:
The standard instances of Integral are Integer (unbounded or mathematical integers, also known as "bignums") and Int (bounded, machine integers, with a range equivalent to at least 29-bit signed binary)
Wikibooks explains this:
"Integer" is an arbitrary precision type: it will hold any number no matter how big, up to the limit of your machine's memory…. This means you never have arithmetic overflows. On the other hand it also means your arithmetic is relatively slow. Lisp users may recognise the "bignum" type here.
In ghci:
Prelude> factorial 50 :: Int
-3258495067890909184
Prelude> factorial 50 :: Integer
30414093201713378043612608166064768844377641568960512000000000000
So, with Int
it actually overflows. Integer
can hold any number without overflow limited by system's memory.
Upvotes: 9