官术网_书友最值得收藏!

  • The Modern C# Challenge
  • Rod Stephens
  • 631字
  • 2021-08-13 15:23:55

4. Factorials

The following code shows a recursive method for calculating factorials:

// Calculate the factorial recursively.
private long RecursiveFactorial(long number)
{
checked
{
if (number <= 1) return 1;
return number * RecursiveFactorial(number - 1);
}
}

This method first checks whether number is less than or equal to 1. If number ≤ 1, the method returns 1. If number is greater than 1, the method calls itself recursively to calculate (number – 1)! and then returns number times (number – 1)!.

The following code shows a non-recursive method for calculating factorials:

// Calculate the factorial non-recursively.
private long NonRecursiveFactorial(long number)
{
checked
{
long total = 1;
for (long i = 2; i <= number; i++) total *= i;
return total;
}
}

This method initializes the total variable to 1. It then enters a loop where it multiples total by the values between 2 and number. The result is 1 × 2 × 3 × ... × number, which is the factorial.

Some recursive programs may have very large depths of recursion where the method calls itself so many times that the stack memory is exhausted and the program crashes. For example, if a program tried to calculate RecursiveFactorial(1000000), the method would call itself 1 million times. That could exhaust the program's stack space and crash the program.

Fortunately (or unfortunately, depending on how you look at it), the factorial function grows extremely quickly, so the number of recursive calls that will work is limited. These methods use 64-bit long integers to perform their calculations, so they can only hold values up to 9,223,372,036,854,775,807. The value 20! is 2,432,902,008,176,640,000 and the value 21! is too big to fit in a 64-bit integer, so the program can only calculate values up to 20! anyway. That means the recursive version can use, at most, 20 levels of recursion, and the program will never exhaust its stack space.

In general, non-recursive versions of methods are often better than recursive versions because they don't make as many demands on stack memory, but in this example the difference doesn't really matter. The limiting factor for this program is the fact that the factorial method grows so quickly that it can exceed the limits of 64-bit integers.

That brings us to the most important lesson in this example. By default, C# does not check integer operations for overflow. If the result of an integer operation is too big to fit inside the appropriate data type, the program normally does not notice. Instead, it continues merrily along using whatever garbage is present in its variables as if nothing was wrong.

You can force C# to check for integer overflow by placing risky statements inside a checked block, as shown in preceding code. If you omit the checked statements, the factorial methods will try to calculate values for numbers greater than 20. For example, they will report that 21! is -4,249,290,049,419,214,848, which is clearly wrong because it's a negative  number. If you try to calculate factorials for much larger values such as 10,000 or 1 million, the program will exhaust its stack space.

C# ignores integer overflow for performance reasons, although in my tests, using a checked block only increased runtime by about 10%. The moral of all this is that if a certain calculation may cause an integer overflow, then you should place it inside a checked block.

A group of checked blocks does not nest across method calls the way try catch blocks do. For example, if a checked block includes a method call and that method might cause an integer overflow, then the method also needs its own checked block because the calling checked block will not protect it.

Download the Factorials example solution to see additional details.

主站蜘蛛池模板: 平利县| 辉县市| 科技| 昔阳县| 灵山县| 墨脱县| 黄龙县| 谷城县| 大港区| 无极县| 扶沟县| 江川县| 宜章县| 惠州市| 景洪市| 永定县| 资中县| 南平市| 沾益县| 青岛市| 枣庄市| 崇信县| 林州市| 区。| 富宁县| 佛冈县| 伊通| 金华市| 上饶县| 宜兴市| 望谟县| 徐汇区| 藁城市| 全椒县| 珲春市| 石家庄市| 宁河县| 通榆县| 鲁甸县| 农安县| 常山县|