微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么N!当 N>=34 时停止溢出 32 位输出变量?

如何解决为什么N!当 N>=34 时停止溢出 32 位输出变量?

大数的变量溢出有什么限制吗?一本关于阶乘的 C 书中有一个练习。

我的代码

#include <stdio.h>

int main(void) {
    unsigned int n,fn,counter;
    //puts("enter nonnegative int");
    //scanf("%u",&n);
    n = 1;
    while (n != 34) {
        fn = n;
        counter = n - 1;
        while (counter > 0) {
            fn *= counter;
            counter--;
        }
        printf("%u\n",fn);
        n++;
    }
}

注释行用于调试。

代码打印从 n(1) 到 34 ('while(n!=34)') 的数字的阶乘,但如果将其增加到 36 之类的值,它将在前 34 个输出后仅打印 0。我知道大部分输出都溢出了,并且对这些大数字的控制非常糟糕。

但是,我想知道导致这些零发生的限制是什么。

解决方法

你抱怨

N!N>=34

时停止溢出 32 位输出变量

意思是,在34!之后,结果一直保持为0。

嗯,答案是它不会停止溢出。发生的情况是显示的值,即从 N=34 开始的除法 N! / 2^32 的余数变为 0,并且永远不会改变。

为了理解它是如何发生的,让我们从一个使用十进制数的例子开始。我们将显示 N 的结果!使用只有两位数的显示:

阶乘 实际结果 显示结果 注意事项
1! 1 01
2! 2 02
3! 6 06
4! 24 24
5! 120 20 溢出!
6! 720 20 溢出!
7! 5040 40 溢出!
8! 40320 20 溢出!
9! 362880 80 溢出!
10! 3628800 00 溢出,显示值为00
11! 39916800 00 溢出,显示的值仍然是00
12! 479001600 00 溢出,显示值仍然是00
.. .. 00 显示值永远为00

如您所见,显示在 5! 处溢出,我们还可以注意到结果是 5*2=10 的倍数。出于显而易见的原因,所有后续结果都将是 5*2=10 的倍数,因此它们的尾随为 0。
但是当我们达到 10! 时,会出现一个特殊情况:结果变成 (5^2)*(2^2)=10^2=100 的倍数,因此,无论我们之后执行的乘法是什么,显示的值始终为 {{ 1}}。

记住这个信息:当结果开始具有 B^N 的公因数时,我们达到了 all-0 条件,其中

  • 00 是表示 (10) 的基础
  • B 是显示的位数 (2)

所以,在这种情况下,N


同样的推理可以使用二进制 (base-2) 表示来完成,受限于 10^2 的大小。当结果开始具有 unsigned int 的公因数时,我们将达到全 0 条件。

结果什么时候会变成B^N = 2^32的倍数?让我们计算一下引入 2 的幂的乘法:

  • 2^32 将添加因子 2(总共 2^1)
  • 2! 将添加一个因子 2^2(总共 2^3)
  • 4! 将添加因子 2(总共 2^4)
  • 6! 将添加一个因子 2^3(总共 2^7)
  • 8! 将添加因子 2(总共 2^8)
  • 10! 将添加一个因子 2^2(总共 2^10)
  • 12! 将添加因子 2(总共 2^11)
  • 14! 将添加一个因子 2^4(总共 2^15)
  • 16! 将添加因子 2(总共 2^16)
  • 18! 将添加一个因子 2^2(总共 2^18)
  • 20! 将添加因子 2(总共 2^19)
  • 22! 将添加一个因子 2^3(总共 2^22)
  • 24! 将添加因子 2(总共 2^23)
  • 26! 将添加一个因子 2^2(总共 2^25)
  • 28! 将添加因子 2(总共 2^26)
  • 30! 将添加一个因子 2^5(总共 2^31)
  • 32! 将添加因子 2(总共 2^32)

从现在开始,阶乘将始终是 34! 的倍数,因此显示的结果中,2^32 的余数将始终为 0。

,

没有限制,溢出永不停止。
只是当 n 到达 34 时,fn 溢出到数字 0。

然后下一个数字将是 35 * 0,下一个 36 * 35 * 0,依此类推。

没什么神秘的。

,

unsigned intUINT_MAX = 4294967295 类型变量的最大值。 在这个过程中,找到 12! 后得到 overflowed。为了处理溢出,C 环绕也称为 modulo wrapping 的值。 例如,

unsigned int x = 4294967295;
x += 1;
printf("%u",x)

输出:0

unsigned int x = 4294967295;
x += 2;
printf("%u",x)

输出:1

这意味着无论何时发生溢出,C 都会环绕值并且永远不会溢出!

但是在这种情况下,同时找到 35!在这个过程中的某个地方,模数变为零。因此,在 34! 之后总是 zero。 因此,零不会出现溢出。

要查找 n! 所需的位,请使用公式 floor(log(n!))+1。 然后使用所需的数据类型。

,

编辑,正如 Eric 所指出的,最大的 unsigned int 值可能最容易被显示为

printf("%u\n",UINT_MAX);

***** 上面编辑 - 下面的原始答案 *****

fn 是具有最大值的 unsigned int 类型。

要找出 unsigned int 尝试的最大值

printf("%d",sizeof(unsigned int));

此命令将告诉您分配给 unsigned int 的内存中的字节数 - 如果该数字为 n,则最大的 unsigned int 值将是 2^{8n}-1 - 或 2 的 8n 次方然后减去 1。

如果您想要更大的数字,那么您可以测试其他类型,例如 long

如果您乐于获得近似值,那么您可以使用 double 类型,它比任何 int 类型都要高得多。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。