【问题标题】:Simple C++ factorial program简单的 C++ 阶乘程序
【发布时间】:2015-07-27 16:29:04
【问题描述】:

作为作业,我需要一个程序来读取一个非负整数并计算和打印它的阶乘。 到目前为止,我编写了代码,但如果我尝试输入 50!结果为 0。它适用于较小的数字。任何帮助将不胜感激。

#include <iostream>

using namespace std;

int main()
{
    int counter = 1;
    int number;

    cout << "Please enter a number: ";
    cin >> number;

    int factorial = number;
    while (counter != number)
    {
        factorial = factorial * (number - counter);
        counter++;

    }

    cout << "The factorial of " << number << "! is: " << factorial << endl;
    return 0;
}

【问题讨论】:

  • 50 的结果应该是什么?您的作业需要能够计算出阶乘的最大数字是多少?
  • int 的最大值是 INT_MAX,通常略高于 20 亿(在具有 32 位 int 的系统上,这几乎是所有普通系统)。​​
  • 它没有说具体的数字。我必须使用 while 语句和到目前为止我学到的变量类型:int、double/float 或 bool。

标签: c++ while-loop integer factorial


【解决方案1】:

50!30414093201713378043612608166064768844377641568960512000000000000,对于 int 来说太大了。由于整数溢出,结果为0。

【讨论】:

  • 由于整数溢出...以及 50!能被 2^32 整除。
  • @im2shae 请注意,50!远大于 unsigned long long,如果您需要精度,我会推荐 BigInteger 类。
  • @im2shae 如果您不关心精确度,双重可以工作
  • @awesomeyi 它确实有效。虽然,一个看起来很奇怪的数字,但它不是 0。我会带来两种解决方案,看看他想要哪一个。非常感谢大家的帮助。
  • 它可以与 double 一起使用,但非常错误,因为通过将不精确的数字多次乘以 (* 46 * 47 * 48 * 49 * etc) 会损失很多精度。这是一个学习的好机会,但在现实世界中,我更喜欢明显错误的东西(值为 0),而不是偷偷摸摸的精度问题,我需要用一个令人印象深刻的计算器实际仔细检查所有 50 位数字。
【解决方案2】:

如果你想计算大阶乘,你需要使用一些 BigInteger 类。 看这个:Thread

【讨论】:

  • 谢谢,但我对课程还不熟悉。
  • 如果你想使用c++,我建议你尽快熟悉它们。与此同时,您可以编写自己的函数来处理大数字。
【解决方案3】:

已经有了一些答案。然而,他们都缺乏提供一些(恕我直言)重要的事实:

无论您选择多大的结果类型,您的程序能够计算的内容总是有限的。

在普通袖珍计算器上 69!是他们可以显示的最大阶乘(因为它是具有两位数指数的最大阶乘)。要求更大的东西只会导致错误或 NaN。实际上这很好,因为在实践中你很少需要如此精确的巨大阶乘。通常可以使用斯特林近似或使用其他技巧来避免冗长的计算(顺便说一句,69!也是袖珍计算器的一个很好的基准,因为在较慢的计算器上它可能已经花费了几秒钟)。

结论:您的代码非常适合合理的输入。如果你真的需要更高阶的阶乘,有办法,但我想你的作业并没有真正要求它。而且,无论你怎么做,你总是会达到一个极限。因此,为了使您的代码“无错误”,我会添加类似

的内容
assert(number < 50 && "Sorry, this number is too big");

在计算之前。

【讨论】:

  • +1,但 50 是一个相当神奇的数字,并且不可移植,因为 int 的大小因不同系统而异。我猜数学倾向可能会让人联想到std::numeric_limits 的可移植断言。
  • @ChristianHackl 是的,当然。我不知何故“把它留给感兴趣的读者”,用更有意义的东西代替50。比破碎更好的魔法:P
【解决方案4】:

选择不超过变量“阶乘”中存储的值的更大范围的数据类型

您应该将其声明为,**long long int factorial = number ;**

现在,它显示为零,因为 int(这里有符号)因此对于将 int 数据类型存储为 2byte 的系统,它的范围从 -32568 到 +32567。 在修饰符“long long”的帮助下,您实际上将其存储字节增加到 8 字节,从而扩大了范围。

现在,如果要存储在任何变量中的值超出其范围,则它会通过从最小范围(这里是 -32568)开始存储的值来执行循环旋转。

您还可以使用 , **unsigned long long int factorial = number ;** 甚至使其范围更大。无符号将其负范围与正范围一起计算,从而导致更大的正范围。

【讨论】:

    【解决方案5】:

    在类或结构中尝试双精度或长数据类型。您还必须相应地修改您的代码。

    【讨论】:

    • Double 显然太小了。写答案时使用一些标点符号。
    • 对不起我的英语。是的 double 太小了,我认为最好使用具有多个 long long 数据类型的类或结构并相应地修改代码
    【解决方案6】:

    阶乘只会产生非常大的数字。您可以尝试使用更广泛或可能更广泛的数据类型(如long),但这只会推迟问题。它在某个时候失败。也许在51!,也许在60!。我们甚至不要谈论1000!。这就是阶乘的本质。

    当然,针对大数字的专用库可以大大缓解这个问题。但它们不是初学者的东西。

    可以做的很好,但是,是安全地找出你的程序是否达到了你的计算机的限制,如果是这样的话,打印一条错误消息。 C++ 提供了一种称为std::numeric_limits 的机制,它告诉您数据类型可以表示的最大可能值。

    这是一个基于您的代码的简单示例:

    #include <iostream>
    #include <limits> // needed for std::numeric_limits
    
    using namespace std;
    
    int main()
    {
        int counter = 1;
        int number;
    
        cout << "Please enter a number: ";
        cin >> number;
    
        int factorial = number;
        while (counter != number)
        {
            if (std::numeric_limits<int>::max() / factorial < (number - counter)) {
                std::cout << "cannot handle such large numbers\n";
                return 0;
            }       
            factorial = factorial * (number - counter);
            counter++;
    
        }
    
        cout << "The factorial of " << number << "! is: " << factorial << endl;
        return 0;
    }
    

    一旦检测到错误情况会发生什么在这里并不重要,重要的是如何检测到它:

    std::numeric_limits<int>::max() / factorial < (number - counter)
    

    这可以防止整数溢出。它在数学上等价于:

    std::numeric_limits<int>::max() < factorial * (number - counter) // wrong!
    

    但是,后一个版本显然不起作用,因为factorial * (number - counter) 可能已经产生了溢出。通过将右边的乘法转换为左边的除法,您可以优雅地避免这个问题。


    顺便说一句,如果用户输入一个非常大的数字,所有这些仍然没有用。因此,您应该在使用number 之前检查std::cin 的状态,如果输入无法解释为int,同样会打印错误消息。这使您的程序更加健壮。如果有人输入大数字,它不会简单地崩溃或产生无意义的结果。

    【讨论】:

      猜你喜欢
      • 2021-11-13
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多