C语言判断2的幂函数对2^29返回错误,floor(x)与x不等问题求解
问题根本原因
- 浮点数计算的固有精度误差
log()是自然对数函数,其计算结果是近似值,你通过log(n)/log(2)计算以2为底的对数时,两次近似值的除法运算会进一步放大误差。你看到printf输出两个值均为29.000000,是因为printf默认仅保留6位小数,这个微小的误差量级远小于1e-6,无法通过普通小数打印观测到,但实际内存存储的二进制值确实存在差异。
你观测到的x和floor(x)的比特位差异,不是floor函数修改了精确值,而是你的x本身就不是精确的29:从你给出的十六进制值(x86平台为小端序)可以推算,x的实际值是精确29加上最小的双精度浮点数步进(1 ULP),属于计算产生的合理误差。 - x86平台的浮点数计算特性
x86架构的FPU默认使用80位扩展精度进行中间运算,寄存器中存储的临时值精度高于内存中存储的64位double,当临时值回写到内存做舍入时,也可能引入额外的精度偏差。
你提到替换为log10()后运行正常,只是该计算路径下的误差刚好落在舍入阈值内,属于巧合,不具备通用性,换其他测试用例仍然可能出错。
修复方案
最优方案:使用位运算(无任何精度问题,性能最高)
2的正整数次幂的二进制表示有且仅有一个1,因此可以通过位运算直接判断:
bool isPowerOfTwo(int n) { // 排除小于等于0的情况,n&(n-1)会消掉最低位的1,如果结果为0说明只有一个1 return n > 0 && (n & (n - 1)) == 0; }
备选方案:保留对数计算,增加误差容限
如果一定要使用对数计算的方式,建议直接使用标准库的log2()(比手动计算log(n)/log(2)精度更高),同时增加误差容限避免直接判等:
#include <math.h> #include <stdbool.h> bool isPowerOfTwo(int n) { if (n <= 0) return false; double x = log2(n); // 允许1e-8的误差范围 return fabs(x - round(x)) < 1e-8; }
内容的提问来源于stack exchange,提问作者Quang Dao
相关产品推荐
相关产品推荐

