C语言中使用pow函数导致循环长度缩短、无法调用menu的原因是什么
问题原因分析
你遇到的程序卡住问题是几个错误叠加导致的,核心原因如下:
- 首先
pow是浮点数计算函数,返回值为double类型,存在两个严重问题:- 精度丢失:
double只有53位有效尾数,大于2^53的整数无法被精确表示,计算结果会出现偏差 - 溢出风险:当
loop值大于1023时,pow(2, loop)已经超出了double的最大可表示范围,会返回正无穷inf,将inf转为int属于C语言标准定义的未定义行为,绝大多数编译环境下会得到32位int的边界值(最大值2147483647或者最小值-2147483648)
- 精度丢失:
- 其次你的
prime_check函数存在两个设计缺陷:- 没有做输入合法性校验:质数定义是大于1的自然数,你没有对传入的负数、小于2的数做拦截,直接进入循环逻辑
- 循环条件存在整数溢出风险:当传入的
num是2147483647(32位int最大值)时,循环变量i增加到46341时,i*i的计算结果为2147488281,超出32位int的最大值,整数溢出后会变为负数,此时i*i <= num(负数永远小于正数)的条件永远成立,程序直接进入死循环,这就是你看到程序卡住的根本原因
- 额外逻辑错误:梅森素数的定义是
2^p -1形式的素数,要求p本身必须是素数,你现在不管p是不是素数都计算2^p -1,完全没有必要,且当p大于31时,2^p -1已经超出了32位int的存储范围,就算不用pow也无法正确存储。
修复方案
你可以按照以下步骤修改代码即可解决问题:
- 给
prime_check函数增加输入校验,同时修改循环条件避免乘法溢出:
int prime_check(int num) { // 增加参数合法性校验,小于等于1的数直接返回0 if (num <= 1) return 0; int i, is_prime = 1; // 把i*i <= num改成i <= num /i,避免乘法溢出 for (i = 2; i <= num / i; i++) { if (num % i == 0) { is_prime = 0; // 找到因数后直接break,没必要继续循环 break; } } return is_prime; }
- 替换
pow函数的整数幂计算,先判断范围再处理:
// 把marsenne=prime_check(pow(2, loop)-1)替换成以下逻辑 int marsenne = 0; if (loop < 31 && prime_check(loop)) { // 32位int下2^31已经溢出,且梅森素数要求p本身是素数 int mersenne_num = (1 << loop) - 1; marsenne = prime_check(mersenne_num); }
- 额外可以删除
prime_total里给非素数的关联标志位清零的重复逻辑,因为前面计算的时候如果loop不是素数,marsenne等变量已经是0了,没必要重复赋值。
内容的提问来源于stack exchange,提问作者Punz
相关产品推荐
相关产品推荐

