不应使用 == 或 != 判断浮点数是否相等
10.5.2 ID_illFloatComparison
用 == 或 != 判断浮点数(float、double、long double)是否相等会得到非预期的结果。
一般来说,除了可以记作 a * 2n(a、n 为整数)的浮点数值可以被精确存储之外,其他均为近似值,如 0、1、2.5、… 可以被精确存储,而除此之外绝大部分数值如 0.1、0.2、0.3、… 只能存储其近似值。
示例:
float f = 1;
f /= 10;
if (f == 0.1) { // Non-compliant, do not use ‘==’ or ‘!=’
cout << "OK";
} else {
cout << "Oops";
}
输出 Oops,这是因为 f == 0.1 在运算时将变量和常量转为 double 型,而转换过程中生成了两个不同的对 0.1 的近似值(如 0.10000000149011611938 和 0.10000000000000000555),其结果自然为 false,这非常容易造成意料之外的错误,所以判断浮点数是否相等不应直接使用 == 或 != 运算符,即使浮点数可以被精确存储。
解决方法:
bool feq(float a, float b, float e = 0.000001f) {
return fabs(a - b) < e;
}
可以利用 feq 函数判断浮点数 a 和 b 是否相等,如果两个浮点数的差值非常小则可以认为相等,其中 fabs 是计算浮点数差值绝对值的函数,如果差值绝对值小于 e 则认为相等,否则不等。
if (feq(f, 0.1)) { // Compliant
cout << "OK";
}
在实际代码中,对于 float、double、long double 等不同的浮点类型,可以分别使用在标准头文件 float.h 中定义的宏 FLT_EPSILON、DBL_EPSILON、LDBL_EPSILON 作为 e 的值,在 C++ 代码中,也可以使用标准模板库提供的 std::numeric_limits<T>::epsilon(),其中 T 为指定的浮点类型。
参考
CWE-1077
MISRA C 2004 13.3
MISRA C++ 2008 6-2-2