正确地测试两个浮点数是否相等是包括我在内的很多人都不完全理解的。然而,今天,我想到了一些标准容器如何用operator<
来定义相等性。我总是看到人们在平等方面存在问题,但从未与其他关系比较相提并论。甚至可以使用silent versions个,其中包括除了平等和不平等之外的所有内容。
假设operator<
“正常”工作,与operator==
不同,我们为什么不能这样做:
bool floateq(float a, float b) {
//check NaN
return !(a < b) && !(b < a);
}
事实上,我确实运行了一个带有双重额外重载的测试,如here所示,它似乎与将它们与operator==
进行比较有相同的缺陷:
std::cout << "float->double vs double: "
<< floateq(static_cast<double>(0.7f), 0.7) << " "
<< (static_cast<double>(0.7f) == 0.7) << "\n";
输出:
float-&gt; double vs double:0 0
我是否担心使用所有比较运算符,或者还有其他方面比较我不能正确理解的浮点数?
答案 0 :(得分:16)
==
,<
,>
,<=
,>=
和!=
运算符可以很好地处理浮点数。
您似乎有一个前提,即某些 <
的合理实现应该比较(double)0.7f等于0.7。不是这种情况。如果您将0.7f
投射到double
,则会获得0x1.666666p-1
。但是,0.7
等于0x1.6666666666666p-1
。这些在数值上并不相同;事实上,(double)0.7f
比0.7
小得多 - 他们比较平等是荒谬的。
使用浮点数时,重要的是要记住它们是浮点数,而不是实数或有理数或任何其他类似的东西。您必须考虑他们的属性,而不是每个人都希望他们拥有的属性。这样做可以自动避免使用浮点数的大多数常见“陷阱”。
答案 1 :(得分:2)
使用浮点数时,关系运算符具有含义,但它们的含义不一定与实际数字的行为一致。
如果浮点值用于表示实际数字(它们的正常目的),则运算符的行为倾向如下:
x > y
和x >= y
都暗示x
应该表示的数字量可能大于y
,最差可能不会少于y
比x < y
。
x <= y
和x
都暗示y
应该表示的数字量可能小于y
,最差的可能不是远远大于x == y
。
x
表示y
和x
所代表的数字量彼此无法区分
请注意,如果float
的类型为y
,而double
的类型为double
,则float
参数为float
时,将实现上述含义施放到double
。但是,在没有特定强制转换的情况下,C和C ++(以及许多其他语言)会在执行比较之前将float f = 16777217;
double d = 16777216.5;
操作数转换为float
。这种转换将极大地降低报告操作数“无法区分”的可能性,但是将极大地增加比较将产生与预期数字实际指示的结果相反的结果的可能性。例如,考虑一下
double
如果两个操作数都转换为d
,则比较将指示值无法区分。如果它们被转换为f
,则比较将指示float f = 1E20f;
float f2 = f*f;
double d = 1E150;
double d2 = d*d;
更大,即使值f2
应该表示稍大。作为一个更极端的例子:
float
Float d2
包含1E40的最佳double
表示。 Double d2 is hundreds of orders of magnitude greater than that represented by
包含1E400的最佳, but
表示。由. By contrast, converting both operands to float would yield
f2 float f2 = f1 / 10.0;
(双)f2&gt;表示的数值量。 d2 {{1}} f2 ==(float)d2`,正确报告值无法区分。
答案 2 :(得分:1)
以下代码(我更改后编译:特别是对floateq
的调用已更改为floatcmp
)打印出float->double vs double: 1 0
,而不是0 0
(如同期望将这两个值作为浮点数进行比较时。)
#include <iostream>
bool floatcmp(float a, float b) {
//check NaN
return !(a < b) && !(b < a);
}
int main()
{
std::cout << "float->double vs double: "
<< floatcmp(static_cast<double>(0.7f), 0.7) << " "
<< (static_cast<double>(0.7f) == 0.7) << "\n";
}
然而,对于标准库而言,重要的是operator<
定义了一个严格的弱排序,它实际上对浮点类型有效。
相等的问题是,当四舍五入到4或6个位置时,两个值可能看起来相同但实际上完全不同并且比较为不相等。
答案 3 :(得分:1)
Float和double都是科学记数法的二进制等值,具有固定数量的有效位。如果计算的无限精度结果不能精确表示,则实际结果是最接近可精确表示的结果。
这有两个很大的陷阱。
(a + b) + c
不一定与a + (b + c)
相同您需要选择一个大于预期舍入误差的比较容差,但要小到足以使程序可以将公差范围内的数字视为相等。
如果没有这样的容差,则表示您使用了错误的浮点类型,或者根本不应该使用浮点数。 32位IEEE 754具有如此有限的精度,因此找到合适的容差非常具有挑战性。通常,64位是更好的选择。
答案 4 :(得分:-2)
通常,浮点数的所有比较操作都应在指定的精度限制内完成。否则,您可能会被累积的舍入误差所困扰,这种误差在低精度时看不到,但是比较运算符会将其考虑在内。 排序通常无关紧要。
另一个代码示例确实显示您的比较不起作用(http://ideone.com/mI4S76)。
#include <iostream>
bool floatcmp(float a, float b) {
//check NaN
return !(a < b) && !(b < a);
}
int main() {
using namespace std;
float a = 0.1;
float b = 0.1;
// Introducing rounding error:
b += 1;
// Just to be sure change is not inlined
cout << "B after increase = " << b << endl;
b -= 1;
cout << "B after decrease = " << b << endl;
cout << "A " << (floatcmp(a, b) ? "equals" : "is not equal to") << "B" << endl;
}
输出:
B after increase = 1.1
B after decrease = 0.1
A is not equal toB