在C++编程中,浮点数由于二进制存储的特性,无法精确表示所有十进制小数,因此直接通过等于号判断两个浮点数是否相等往往会出现不符合预期的结果,需要采用专门的方法处理精度误差后再进行比较。

浮点数精度误差产生的原因
计算机底层采用二进制存储数据,而很多十进制小数转换为二进制时是无限循环的,比如0.1转换为二进制后是0.0001100110011...,受限于浮点数的存储位数,只能截取有限位存储,这就产生了精度损失。当两个浮点数经过多次运算后,这种精度损失会累积,导致原本数学上相等的两个数在计算机中存储的值存在微小差异。
直接比较的问题示例
先看一个直接比较两个浮点数的错误示例:
#include <iostream>
using namespace std;
int main() {
float a = 0.1f + 0.2f;
float b = 0.3f;
// 直接比较,预期结果为相等,实际会输出不相等
if (a == b) {
cout << "a和b相等" << endl;
} else {
cout << "a和b不相等" << endl;
}
return 0;
}
运行上述代码,会输出a和b不相等,因为0.1加0.2的结果在计算机中存储的值和0.3的存储值存在微小差异,直接比较等于号会判定为不相等。
常用的浮点数相等判断方法
1. 绝对误差比较法
这种方法的核心思想是判断两个浮点数的差值的绝对值是否小于一个极小的阈值,这个阈值通常称为epsilon。适用于比较的两个数数量级相近的场景。
#include <iostream>
#include <cmath>
using namespace std;
// 定义绝对误差阈值
const float EPSILON = 1e-6f;
bool isFloatEqualAbsolute(float x, float y) {
// 计算两个数差值的绝对值,判断是否小于阈值
return fabs(x - y) < EPSILON;
}
int main() {
float a = 0.1f + 0.2f;
float b = 0.3f;
if (isFloatEqualAbsolute(a, b)) {
cout << "a和b相等" << endl;
} else {
cout << "a和b不相等" << endl;
}
return 0;
}
2. 相对误差比较法
当两个浮点数的数量级相差较大时,绝对误差法的固定阈值可能不适用,此时可以采用相对误差比较,判断差值的绝对值和两个数的最大值的比值是否小于阈值。
#include <iostream>
#include <cmath>
using namespace std;
const float REL_EPSILON = 1e-6f;
bool isFloatEqualRelative(float x, float y) {
float diff = fabs(x - y);
// 如果两个数本身很小,直接用绝对误差判断
if (diff < REL_EPSILON) {
return true;
}
// 计算相对误差,判断是否在阈值范围内
float maxVal = fmax(fabs(x), fabs(y));
return diff / maxVal < REL_EPSILON;
}
int main() {
float a = 100000.0f + 0.000001f;
float b = 100000.0f;
if (isFloatEqualRelative(a, b)) {
cout << "a和b相等" << endl;
} else {
cout << "a和b不相等" << endl;
}
return 0;
}
3. 结合绝对误差和相对误差的比较法
为了覆盖更多场景,可以将两种方法结合,先判断绝对误差,再判断相对误差,适用性更强。
#include <iostream>
#include <cmath>
using namespace std;
const float ABS_EPSILON = 1e-6f;
const float REL_EPSILON = 1e-6f;
bool isFloatEqual(float x, float y) {
float diff = fabs(x - y);
// 绝对误差判断
if (diff <= ABS_EPSILON) {
return true;
}
// 相对误差判断
float maxVal = fmax(fabs(x), fabs(y));
return diff <= maxVal * REL_EPSILON;
}
int main() {
float a = 0.1f + 0.2f;
float b = 0.3f;
float c = 100000.0f + 0.000001f;
float d = 100000.0f;
cout << "a和b比较结果:" << (isFloatEqual(a, b) ? "相等" : "不相等") << endl;
cout << "c和d比较结果:" << (isFloatEqual(c, d) ? "相等" : "不相等") << endl;
return 0;
}
注意事项
- epsilon的取值需要根据实际业务场景调整,不同的精度要求阈值不同,没有通用的固定值。
- 对于double类型的浮点数,epsilon的取值通常要比float类型小,比如可以用1e-12作为绝对误差阈值。
- 如果比较的是两个浮点数是否不相等,只需要对相等判断的结果取反即可,不要单独写不相等的判断逻辑。