我对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);
}
答案 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
的范围是0
至2^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^32
或1,932,053,504
的其余部分,因为这就是result
在最后一个之后的增加量(在这种情况下,仅)循环回到0
。
现在,33!
或34!
代表了难以想象的大值,并使result
多次溢出。只需将33!
除以2^32
,就可以计算出2.02e27
导致溢出的次数,从而导致大约34
溢出。但是,您的问题与发生多少溢出无关,而与最后一次溢出后的余数有关。在这种情况下,它等于33! % 2^32 = 2,147,483,648
。我们可以对2^32
:34! % 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。