For my curiosity I am writing a code to find the factorial of large number eg:25. For that I wrote the below code. I use BigInteger as it can store value of any length as define per memory.
int a = 13;
BigInteger factorial1 = a == 0 ? 1 : Enumerable.Range(1, a).Aggregate((i, j) => i * j); // Aggregate function not working properly after 12 factorial
Console.WriteLine(factorial1);
But surprisingly, I don't get the correct answer. I tried for smaller number upto 12 factorial and it gives correct answer but for 13 and beyond, the answer is wrong.
I have tried this very simple code which gives the correct answer.
BigInteger factorial3 = 1;
while (n > 0)
{
factorial3 = factorial3 * n;
--n;
}
Console.WriteLine(factorial3);
But the problem here is that BigInteger is immutable. So the above code will have a large memory foot print which is not desirable.
CodePudding user response:
You should specify starting aggregation value, it should be BigInteger.One
:
BigInteger factorial1 = a == 0 ? 1 : Enumerable
.Range(1, a)
.Aggregate(BigInteger.One, (i, j) => i * j);
otherwise for .Aggregate((i, j) => i * j)
the result will be of type int
(since both i
and j
are int
) and only then (after integer overflow, 13! = 6227020800 > int.MaxValue = 2147483647
) it will be cast to BigInteger
.
CodePudding user response:
Enumerable.Range produces int
s, not BigInteger
s. Enumerable.Range(1, a).Aggregate((i, j) => i * j);
uses Int32
throughout and ends up overflowing after 13.
To avoid overflow you need to convert those integers to BigInteger before processing them:
var n=Enumerable.Range(1, 13).Select(i=>(BigInteger)i)
.Aggregate((i, j) => i * j);
This produces 6227020800
Another option is to create your own Range
method that creates BigInteger
values :
public static IEnumerable<BigInteger> BigRange(BigInteger start,int count)
{
for(var i=start;i<start count;i )
{
yield return i;
}
}
...
var n=BigRange(1, 13).Aggregate((i, j) => i * j);