为什么该程序给我SIGFPE?

时间:2018-10-18 14:01:12

标签: c

我在下面的程序中遇到SIGFPE错误,无法想到将其删除的方法。

#include <stdio.h>

unsigned int fact(unsigned long int n)
{
    if (n <= 0)
        return 1;
    return n*fact(n-1);
}


int main(){
    int t,r,x,y,z,sum=0,n;

    scanf("%d",&t);

    for(int l=0; l<t; l++){

        scanf("%d",&n );

        if(n%2==0){
            for(int j=n,i=0,k=n; k>=0&&j>0;i++, j++,k-=2){
                x=fact(n-i);
                y=fact(k);
                z=fact(n-k-i);
                sum=sum+  (x)/(y*z);
            }
            printf("%d\n",sum );
        }

        if(n%2!=0){
            for(int j=n,i=0,k=n; k>=1&&j>0;i++, j++,k-=2){
                x=fact(n-i);
                y=fact(k);
                z=fact(n-k-i);
                sum=sum+  (x)/(y*z);

            }
            printf("%d\n",sum);
        }
        sum=0;

    }
    return 0;
}

我正在尝试计算一个需要阶乘的级数,但这给了我SIGFPE错误。我使用35作为输入。

链接到问题:screen shot

solution

3 个答案:

答案 0 :(得分:4)

35!远远超过了您平台上unsigned int的容量。

然后发生的情况是fact(35)返回0,结果除以0,因此SIGFPE( f 浮动 p oint e < / strong>欺骗”。

考虑使用unsigned long long类型,但这对35也不起作用,因为35!真的很大。

答案 1 :(得分:0)

除了Jabberwocky答案。

溢出不是唯一的原因,哦,有一个“舍入”数字溢出...

fact(34)返回惊人的结果0。

如果它是uint64的返回值和arg

unsigned long long fact(unsigned long long n)

,然后使用

打印结果
printf("%llx",x);

您将得到结果:445da75b00000000

将其转换为int即可得到完美的0。

这是合乎逻辑的,因为您已经将许多偶数相乘,并且有些乘数是2的偶数次幂。

您只需要乘以32 2s即可达到此结果:

  • 2,6,10,14,18,22,26,30,34每个都给您一个2,所以您有9 2s
  • 4,12,20,28分别给您4,即2 * 2,所以它给您8 2s,所以您已经有17 2s
  • 8,24给您8,即2 * 2 * 2,所以您还有6 2s,总共是23 2s
  • 16给您更多4 2s,所以您有27 2s
  • 32给您更多5 2s,所以您恰好有32 2s。

答案 2 :(得分:0)

在您的程序中,您为每个可能的两个任务天数计算二项式系数C(n,k)。公式很简单:

C(n, k) = n! / (k! * (n - k)!)

,但是直接使用该公式有几个问题:中间结果,即阶乘,迅速溢出。无符号的32位整数最多可容纳12!个事实,无符号的64位整数最多可容纳20!个,但是此后,您将需要bignum库。问题陈述指出,任务数N最多为80。我相信选择了此限制,以便您可以使用uint64_t中的<stdint.h>进行所有计算。

还有其他问题; Raymond Chen比我更优雅地概述了它们。他还提出了一种计算二项式系数的简单方法,您可以采用:

uint64_t binom(uint64_t n, uint64_t k)
{
    uint64_t c = 1u;
    uint64_t i;

    for (i = 0; i < k; i++) {
        c *= n - i;
        c /= i + 1;
    }

    return c;
}

哦,您不需要区分N的奇数和偶数。

这个挑战有一个奇怪的地方:N是每个测试用例的唯一参数,它的范围可以从1到80,但最多可以有10,000个测试用例。如果超过80个,N将重复。也许他们希望您将结果记忆在一个数组中,以便第一次计算该结果时将其存储起来,而下次您必须再次针对相同的N计算时,只需使用预先计算的值即可。这样可以节省时间,并且“超过时间限制”是在线竞赛中非常常见的错误。

您还可以尝试计算前几个结果,例如从1到10的N值,然后打印出来。注意到模式吗?看,您根本不需要二项式系数。 :)