简体   繁体   English

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

[英]Here's a program to find the factorial of a number. Why am I getting 0 as the answer? Which datatype should I use instead?

I'm relatively new to C. I made this program to find the factorial of any number. 我是C语言的新手。我制作了此程序,以查找任意数量的阶乘。 Upon executing the program, when I provide 33 as the input - I get 2147483648 as the answer. 执行程序后,当我提供33作为输入时-得到2147483648作为答案。 If I provide 34, I get 0 as the answer. 如果提供34,则答案为0。

Getting to my question - Why did I get 0 as the answer? 回答我的问题-为什么我得到0作为答案? The datatype I've used has a range of 0-4294967295. 我使用的数据类型范围为0-4294967295。 Am I getting 0 cause this is outside the range of unsigned int? 我是否得到0,因为这超出了unsigned int的范围? Which datatype should I use if I want to get a large number as the output? 如果要获取大量的输出,应该使用哪种数据类型?

Compiler used - GCC 8.2.1 使用的编译器-GCC 8.2.1

Here's the code - 这是代码-

#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);
}

33! is actually way out of the range of a 32 bit int , whether signed or unsigned. 实际上超出了有符号或无符号的32位int范围。 12! has a value of 479001600, while 13! 值479001600,而13! has a value of 6227020800 , so you go out of range at 13! 的值为6227020800 ,因此您超出了13!的范围13! .

Note also that result is defined as an int and you return an int from fact . 还要注意, result定义为一个int并且您从fact返回一个int This means you end up with signed integer overflow which invokes undefined behavior . 这意味着您最终会遇到带符号的整数溢出,该溢出会调用未定义的行为 This can be fixed by changing the type of both to unsigned , although you're still limited to 12! 可以通过将两者的类型都更改为unsigned ,尽管您仍限于12! .

You can try using unsigned long long for your types instead. 您可以尝试对类型使用unsigned long long类型。 That will get you up to 20! 那最多可以让您20! . If you want values larger than that, you need to use a bigint library such as GMP. 如果要大于此值,则需要使用bigint库,例如GMP。

The factorial grows very quickly . 阶乘增长很快 13! 13! is already outside the range representable by a 32-bit unsigned integer. 已超出32位无符号整数可表示的范围。 Unsigned arithmetic returns the remainder when a result is not representable -- that is, you get only the least-significant bits of the true mathematical result. 当结果无法表示时,无符号算术将返回余数-也就是说,您仅获得真实数学结果的最低有效位。 You could go a bit higher by using a wider data type, but not much. 通过使用更宽的数据类型,您可以提高一点,但不要太多。 You need an arbitrary-precision arithmetic package and a lot of memory to go much further. 您需要一个任意精度的算术软件包和大量的内存才能继续使用。

As for why the result you get for 34! 至于为什么得到34分的结果呢! is exactly zero , note that among the factors 1 * 2 * 3 * ... * 33 * 34 there are 17 that are multiples of two, 8 of which are also multiples of 4, 4 of which are also multiples of 8, two of which are multiples of 16, and one of which is 32. That's a total of 32 2s in the prime factorization of the mathematical result, so the remainder modulo 2 32 is exactly zero. 正好为零 ,请注意,在因子1 * 2 * 3 * ... * 33 * 34中,有17个是2的倍数,其中8个也是4的倍数,其中4个也是8的倍数其中16的倍数,其中之一是32。在数学结果的质数分解中,总共有32 2s,因此余数模2 32恰好为零。

An unsigned int has a range of 0 to 2^32-1 , or 2^32 = 4,294,967,296 different values. unsigned int的范围是02^32-1 ,或者2^32 = 4,294,967,296不同的值。 Assigning your result a value higher than 2^32-1 = 4,294,967,295 makes the value overflow. result分配大于2^32-1 = 4,294,967,295的值会使该值溢出。 This simply means it loops back to 0 , after which it can increase up to 4,294,967,295 again. 这仅表示它循环回到0 ,之后可以再次增加到4,294,967,295

The first overflow happens when calculating 13! 计算13!时会发生第一次溢出13! , when we would expect the result value to be 13! = 6,227,020,800 ,则我们期望result值为13! = 6,227,020,800 13! = 6,227,020,800 . 13! = 6,227,020,800 However, we did not take the overflow into account. 但是,我们没有考虑溢出。 The value of result will instead equal the remainder of the equation 13! % 2^32 result的值将等于等式13! % 2^32的其余部分13! % 2^32 13! % 2^32 , or 1,932,053,504 , because that's how much result increases after the last (and, in this case, only) loop back to 0 . 13! % 2^321,932,053,504 ,因为这是在最后一次(在这种情况下,仅是)循环回到0之后result增加的数量。

Now, 33! 现在33! or 34! 34! represent values that are are unimaginably large, and make result overflow many times. 表示太大的值,并使result多次溢出。 We can calculate how many times 33! 我们可以计算33!33! causes an overflow simply by dividing it by 2^32 , resulting in around 2.02e27 overflows. 仅仅通过将其除以2^32导致溢出,从而导致大约2.02e27溢出。 However, your question isn't concerned with how many overflows happen, but with the value of the remainder after the last overflow. 但是,您的问题与发生多少溢出无关,而与最后一次溢出后的余数有关。 In this case, it equals 33! % 2^32 = 2,147,483,648 在这种情况下,它等于33! % 2^32 = 2,147,483,648 33! % 2^32 = 2,147,483,648 . 33! % 2^32 = 2,147,483,648 We can do the same for 34 : 34! % 2^32 = 0 我们可以对34做同样的事情34! % 2^32 = 0 34! % 2^32 = 0 . 34! % 2^32 = 0

What this means is that, coincidentally, 2^32 is a proper divisor of 34! 巧合的是,这意味着2^3234!的适当除数34! . Or, isn't this coincidental after all ? 或者说,是不是这个巧合毕竟

Edit : like others have suggested, you should take a look at the GMP Bignum library with no limit in precision arithmetic except that of your machine. 编辑 :像其他人建议的那样,您应该看看GMP Bignum库 ,除了您的计算机外,它在精度算术上没有限制。

Your assumption The datatype I've used has a range of 0-4294967295. 您的假设我使用的数据类型范围为0-4294967295。 is not right. 是不正确的。 you define ans parameter as unsigned int which has range of 0-4294967295 but in your fact function, you are using int which has range of -2,147,483,648 to 2,147,483,647 . 您将ans参数定义为unsigned int ,范围为0-4294967295但在事实函数中,您使用的int范围为-2,147,483,648 to 2,147,483,647

So you should change yout code as this: 因此,您应该这样更改代码:

#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);
}

You can also use unsigned long long instead of unsigned int which will support bigger numbers and is more suitable for factorial calculation. 您也可以使用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);
}

More reading on data types and their range: https://www.tutorialspoint.com/cprogramming/c_data_types.htm 有关数据类型及其范围的更多阅读: https : //www.tutorialspoint.com/cprogramming/c_data_types.htm

You get 0 when you overflow your data types. 当您的数据类型溢出时,您将获得0。 Formally the behaviour on overflowing an int is undefined . 从形式上讲,溢出int的行为是不确定的 If you switch consistently to unsigned types, then the overflow is such that the behaviour is consistent with arithmetic modulo 2 raised to the power of the number of bits in your unsigned type. 如果您始终切换到unsigned类型,则溢出将使行为与将unsigned类型的位数unsigned幂的算术模2一致。

Since a large factorial is a multiple of a power of 2, 0 will be attained for a surprisingly small input, as you observe here. 如您在此处观察到的,由于大阶乘是2的幂的倍数,因此对于令人惊讶的小输入,将获得0。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM