Home > Software design >  LINQ Aggregate function is not working properly after 13 factorial?
LINQ Aggregate function is not working properly after 13 factorial?

Time:12-16

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 ints, not BigIntegers. 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);
  • Related