对于大的数字是否有变量溢出的限制?在一本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。我知道大部分的输出都是溢出的,我对这些大数字的控制非常差。
但是,我想知道是什么限制导致这些零出现。
4条答案
按热度按时间pu82cl6c1#
你抱怨说
当
N>=34
出现以下情况时,N!
停止溢出32位输出变量这意味着在
34!
之后,结果保持为0。答案是它不会停止溢出。所发生的是,所示的值,即除法
N! / 2^32
的余数,从N=34开始变为0,并且永远不会改变。为了理解它是如何发生的,让我们从一个使用十进制数的例子开始。我们将显示N的结果!使用只有两个数字的显示器:
| 阶乘|实际结果|显示结果|注意事项|
| --------------|--------------|--------------|--------------|
| 一!|1| 01||
| 两个!|2| 02||
| 三!|六|06||
| 四个!|二十四|二十四||
| 五!|一百二十|二十|溢出!|
| 六个!|七二零|二十|溢出!|
| 七个!|五零四零|四十|溢出!|
| 八!|40320|二十|溢出!|
| 九!|362880|八十|溢出!|
| 十个!|3628800| 00|溢出,显示值为00!|
| 十一个!|39916800| 00|溢出,显示值仍为00!|
| 十二个!|479001600| 00|溢出,显示值仍为00!|
| ...|...| 00|显示的值将永远为00|
正如您所看到的,显示在
5!
处溢出,我们还可以注意到结果是5*2=10
的倍数。由于显而易见的原因,所有后续结果都将是5*2=10
的倍数,因此它们将有一个尾随0。但是当我们到达
10!
时,出现了一个特殊的情况:结果变成了(5^2)*(2^2)=10^2=100
的倍数所以,无论我们之后执行什么乘法,显示的值总是00
。记住这个信息:当结果开始具有公因子B^N时,我们达到了 *all-0条件 *,其中
B
是表示(10)的基N
是显示的位数(2)所以,在这种情况下,
10^2
。同样的推理也可以使用二进制(base-2)表示来完成,但受限于
unsigned int
的大小。当结果的公因子为B^N = 2^32
时,我们将达到全0条件。什么时候结果会变成
2^32
的倍数?让我们计算引入2的幂的乘法:2!
将添加因子2(总共2^1)4!
将添加一个因子2^2(总共2^3)6!
将添加因子2(总共2^4)8!
将添加一个因子2^3(总共2^7)10!
将添加因子2(总共2^8)12!
将添加一个因子2^2(总共2^10)14!
将添加因子2(总共2^11)16!
将添加一个因子2^4(总共2^15)18!
将添加因子2(总共2^16)20!
将添加一个因子2^2(总共2^18)22!
将添加因子2(总共2^19)24!
将添加一个因子2^3(总共2^22)26!
将添加因子2(总共2^23)28!
将添加一个因子2^2(总共2^25)30!
将添加因子2(总共2^26)32!
将添加一个因子2^5(总共2^31)34!
将增加一个因子2(总共2^32)从现在开始,阶乘将始终是
2^32
的倍数,因此显示的结果,N! / 2^32
的余数将始终为0。odopli942#
没有限制,溢出永远不会停止。
只是当
n
到达34时,fn
溢出到数字0。然后下一个数字将是35 * 0,下一个36 * 35 * 0,依此类推。
没什么神秘的
xoshrz7s3#
unsigned int
,UINT_MAX = 4294967295
类型变量的最大值。在这个过程中,在找到12!
之后,它得到overflowed
。为了处理溢出,C包围了也称为modulo wrapping
的值。比如说输出:
0
输出:
1
这意味着无论何时发生溢出,
C
都会包围该值,并且它永远不会溢出!但在这种情况下,同时找到35!在该过程中的某处模变为零。因此,在
34!
之后总是zero
。因此,零不会出现溢出。要查找
n!
所需的位,请使用公式floor(log(n!))+1
。然后使用所需的数据类型。smtd7mpg4#
编辑,正如Eric所指出的,最大的unsigned int值可能最容易通过
编辑上方-下方原始答案
fn
的类型为unsigned int
,将具有最大值。要找出
unsigned int
,请尝试这个命令将告诉你内存中分配给
unsigned int
的字节数-如果这个数字是n
,那么最大的unsigned int
值将是2^{8 n}-1 -或者2的8 n次方,然后减去1。如果你想有一个更大的数字,那么你可以测试其他类型,如
long
。如果你喜欢得到近似值,那么你可以使用
double
类型,它将比任何int
类型高得多。