这是一个查找数字阶乘的程序。为什么我得到0作为答案?我应该改用哪种数据类型?

时间:2019-02-27 14:02:51

标签: c gcc

我对C还是比较陌生。我编写了这个程序来查找任意数量的阶乘。执行程序后,当我提供33作为输入时-得到2147483648作为答案。如果我提供34,则答案为0。

回答我的问题-为什么我得到0作为答案?我使用的数据类型范围为0-4294967295。我是否得到0,因为这超出了unsigned int的范围?如果要获取大量的输出,应该使用哪种数据类型?

使用的编译器-GCC 8.2.1

这是代码-

#include<stdio.h>
#include<stdlib.h>
int fact(unsigned int n)
{
        int result;
        if(n==0 || n==1)
                result=1;
        else
                result=n*fact(n-1);
        return result;
}
int main()
{
        unsigned int n,ans;
        printf("Enter n:");
        scanf("%u",&n);
        ans=fact(n);
        printf("Factorial of %u:%u",n,ans);
}

5 个答案:

答案 0 :(得分:6)

33!实际上超出了32位int的范围,无论是带符号的还是无符号的。 12!的值为479001600,而13!的值为6227020800,因此您超出了13!的范围。

还要注意,result被定义为int,并且您从int返回了fact。这意味着您将遇到带符号的整数溢出,该整数调用undefined behavior。可以通过将两者的类型都更改为unsigned来解决此问题,尽管您仍然限于12!

您可以尝试将unsigned long long用作类型。这样可以使您达到20!。如果您想要更大的值,则需要使用bigint库,例如GMP。

答案 1 :(得分:3)

阶乘增长非常快。 13!已超出32位无符号整数可表示的范围。当结果无法表示时,无符号算术将返回余数-也就是说,您仅获得真实数学结果的最低有效位。通过使用更宽的数据类型,您可以提高一点,但不要太多。您需要一个任意精度的算术软件包和大量内存才能进一步使用。

至于为什么要获得34的结果!恰好为零,请注意,在因子1 * 2 * 3 * ... * 33 * 34中,有17个是2的倍数,其中8个也是4的倍数,其中4个也是倍数取8的整数,其中两个为16的倍数,其中一个为32。在数学结果的质因数分解中,总数为32 2s,因此余数模2 32 恰好为零。

答案 2 :(得分:1)

unsigned int的范围是02^32-1,或者2^32 = 4,294,967,296个不同的值。为您的result分配一个高于2^32-1 = 4,294,967,295的值会使该值溢出。这仅表示它会循环回到0,此后可以再次增加到4,294,967,295

第一次溢出发生在计算13!时,我们期望result的值为13! = 6,227,020,800。但是,我们没有考虑溢出。 result的值将等于等式13! % 2^321,932,053,504的其余部分,因为这就是result在最后一个之后的增加量(在这种情况下,仅)循环回到0

现在,33!34!代表了难以想象的大值,并使result多次溢出。只需将33!除以2^32,就可以计算出2.02e27导致溢出的次数,从而导致大约34溢出。但是,您的问题与发生多少溢出无关,而与最后一次溢出后的余数有关。在这种情况下,它等于33! % 2^32 = 2,147,483,648。我们可以对2^3234! % 2^32 = 0做同样的事情。

巧合的是,34!是{{1}}的适当除数。或者,这不是巧合的after all吗?

编辑:就像其他人建议的那样,您应该看看the GMP Bignum library,除了计算机的精度计算外,其他任何其他方面都没有限制。

答案 3 :(得分:0)

您的假设我使用的数据类型范围为0-4294967295。是不正确的。您将ans参数定义为unsigned int,范围为0-4294967295,但是在事实函数中,您使用的是int,范围为-2,147,483,648 to 2,147,483,647

所以您应该这样更改代码:

#include<stdio.h>
#include<stdlib.h>
unsigned int fact(unsigned int n)
{
        unsigned int result;
        if(n==0 || n==1)
                result=1;
        else
                result=n*fact(n-1);
        return result;
}
int main()
{
        unsigned int n,ans;
        printf("Enter n:");
        scanf("%u",&n);
        ans=fact(n);
        printf("Factorial of %u:%u",n,ans);
}

您也可以使用unsigned long long代替unsigned int,这将支持更大的数字并且更适合阶乘计算。

#include<stdio.h>
#include<stdlib.h>
unsigned long long fact(unsigned int n)
{
        unsigned long long result;
        if(n==0 || n==1)
                result=1;
        else
                result=n*fact(n-1);
        return result;
}
int main()
{
        unsigned int n;
        unsigned long long ans;
        printf("Enter n:");
        scanf("%u",&n);
        ans=fact(n);
        printf("Factorial of %u:%u",n,ans);
}

进一步了解数据类型及其范围: https://www.tutorialspoint.com/cprogramming/c_data_types.htm

答案 4 :(得分:0)

当数据类型溢出时,您将获得0。在形式上,int溢出的行为是 undefined 。如果将一致切换为unsigned类型,则溢出将使行为与将unsigned类型的位数乘幂的算术模2一致

由于大阶乘是2的幂的倍数,因此,如您在此处观察到的那样,对于令人惊讶的小输入,将获得0。