C语言双精度浮点数epsilon比较异常及二进制表示相关疑问
所有问题的根源都是十进制小数与二进制双精度浮点数的表示规则不兼容,双精度浮点数仅保留53位有效二进制位,绝大多数十进制有限小数都是二进制无限循环小数,存储时会触发舍入,不存在精确相等的情况。
疑问1解答
1.0000000001本身就无法被双精度浮点数精确表示,你定义double wit1 = 1.0000000001时,编译器会自动将它舍入到最接近的双精度可表示值,这个舍入后的值减去1.0得到的差值,和你直接写double diff = 0.0000000001舍入得到的双精度值本来就不是同一个数,所以二进制表示存在差异。
你看到的末尾01序列,就是1e-10这个十进制数对应的二进制无限循环小数的舍入截断结果,属于正常现象。
疑问2解答
是的,就是浮点数无法精确表示十进制值导致的。双精度浮点数的有效精度换算成十进制只有15~17位,你打印100位小数时,超过17位之后的内容都是舍入带来的无效数字,自然会出现不规则的数值。
疑问3解答
首先你当前的比较逻辑存在错误:你写的a - b <= EPSILON没有取绝对值,当a < b时差值为负,必然小于正的EPSILON,你运行结果里只命中b - a <= EPSILON就是这个原因;而你没有命中a - b <= EPSILON,恰恰说明1.0000000001舍入后减1.0的结果比你定义的1e-10要大,当然不会触发条件。
不需要对"差值等于epsilon"的情况做特殊处理,浮点数运算场景下几乎不可能出现恰好等于固定epsilon的情况,epsilon的设计本意就是用区间判断代替精确相等判断。
你可以修正判断逻辑为:
// 绝对值判断,适合数值量级不大的场景 if (fabs(a - b) <= EPSILON) { printf("a和b近似相等\n"); } // 如果比较的数值量级波动大,建议用相对误差判断 if (fabs(a - b) <= EPSILON * fmax(fabs(a), fabs(b))) { printf("a和b近似相等\n"); }
另外你写的二进制转换函数逻辑是对的,但双精度浮点数的小数存储位只有52位,你循环输出200位时,超过52位之后的内容都是二进制循环小数的重复位,没有实际意义。
内容的提问来源于stack exchange,提问作者Funny

