C++ 如果操作员<;适用于浮点类型,为什么';我们不能用它来进行平等性测试吗?
正确地测试两个浮点数是否相等,这是包括我在内的许多人都不完全理解的事情。然而,今天,我想到了一些标准容器是如何根据C++ 如果操作员<;适用于浮点类型,为什么';我们不能用它来进行平等性测试吗?,c++,floating-point,equality,C++,Floating Point,Equality,正确地测试两个浮点数是否相等,这是包括我在内的许多人都不完全理解的事情。然而,今天,我想到了一些标准容器是如何根据操作符定义相等的。下面的代码(我更改了它,以便它进行编译:特别是对floatq的调用更改为floatcmp)打印出来float->double vs double:1 0,而不是0 0(正如我们在将这两个值作为浮点值进行比较时所期望的那样) #包括 布尔浮点CMP(浮点a、浮点b){ //查南 返回!(a=y都意味着x应该表示的数值可能大于y,最坏情况下可能不小于y x
操作符定义相等的。下面的代码(我更改了它,以便它进行编译:特别是对floatq
的调用更改为floatcmp
)打印出来float->double vs double:1 0
,而不是0 0
(正如我们在将这两个值作为浮点值进行比较时所期望的那样)
#包括
布尔浮点CMP(浮点a、浮点b){
//查南
返回!(astd::cout一般来说,对浮点数的所有比较操作都应在指定的精度限制内完成。否则,您可能会受到累积舍入误差的影响,这种误差在低精度下是看不到的,但会被比较运算符考虑在内。
这对分类来说通常没有多大关系
另一个代码示例显示比较不起作用()
=
、
、=
和!=
运算符可以很好地处理浮点数
你似乎有这样一个前提:Float和double的一些合理实现都是科学记数法的二进制等价物,具有固定数量的有效位。如果计算的无限精度结果不能精确表示,那么实际结果就是最接近的精确表示结果
这有两大陷阱
许多简单、简短的十进制展开式(如0.1)不能用浮点或双精度表示
在实数运算中相等的两个结果在浮点运算中可能不同。例如,浮点运算不是关联的-(a+b)+c
不一定与a+(b+c)
您需要为比较选择一个公差,该公差大于预期的舍入误差,但足够小,以便在程序中可以将公差范围内的数字视为相等
如果没有这样的容差,则表示您使用了错误的浮点类型,或者根本不应该使用浮点。32位IEEE 754的精度非常有限,因此很难找到合适的容差。通常,64位是更好的选择。使用浮点数时,关系运算符R有含义,但它们的含义不一定与实际数字的行为一致
如果浮点值用于表示实际数字(其正常用途),则运算符的行为通常如下所示:
x>y
和x>=y
都意味着x
应该表示的数值可能大于y
,最坏情况下可能不小于y
x
和x d2。相比之下,将两个操作数转换为浮点将产生
f2==(float)d2`,正确地报告值无法区分
PS——我很清楚IEEE标准要求计算时,浮点值表示两个分数的精确幂,但很少有人将代码float f2=f1/10.0;
视为“将f2设置为两个分数的可表示幂,最接近于f1的十分之一”。该代码的目的是使f2为f1的十分之一。由于不精确,该代码无法完美地实现这一目的,但在大多数情况下,将浮点数视为表示实际数值的数值比将其视为两个分数的幂更有帮助。将浮点数与等式进行比较没有错点值本身。问题是结果可能不符合预期。抱歉,我发布的代码示例主要取自我的测试,该测试链接到。它有两个浮点数的重载和两个双精度的重载。我修改了问题以更好地反映这一点,并保持名称一致。关于实际答案,不应该这一点点的小错误,不管有多少个小数位,都可以由操作符operator@tmyklebu,当NaN不可能时,这是严格的弱排序吗?@chris:确实是。浮点的随机过去前置部分存储在哪里?我只看到符号位、指数和有效位。我必须同意我用的丁有点令人沮丧。更新了。你更新的答案的问题是,你必须以某种方式选择公差。它不能太大,否则你会错误地说两个不同的东西是相等的,不能太小,或者你会错误地说两个“相等”事情是不同的。选择公差,当它可以做的时候,通常需要仔细分析输入和对该输入进行计算时产生的舍入,在这一点上,您可能很明显需要在公差范围内进行比较。我认为这正是我需要的。它没有点击那个铸造浮点数
会使额外的精度部分不同于原来的双精度
。因此,一个必须小于另一个,因此不相等。这也是一种耻辱,因为如果这样做有效的话,我的生活会变得更加轻松:这是一个伟大的问题,也是一个伟大的答案。对于任何编写软件的人来说关于货币,我使用比较if(abs(a-b)<0.001)
。通常情况下,金融系统的利率只有0.5美分,最多只有0.1美分。仔细澄清这一点非常重要。使用浮点数进行金融计算是非常危险的。通常,使用整数或小数进行金融计算
bool floateq(float a, float b) {
//check NaN
return !(a < b) && !(b < a);
}
std::cout << "float->double vs double: "
<< floateq(static_cast<double>(0.7f), 0.7) << " "
<< (static_cast<double>(0.7f) == 0.7) << "\n";
#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";
}
#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
float f = 16777217;
double d = 16777216.5;
float f = 1E20f;
float f2 = f*f;
double d = 1E150;
double d2 = d*d;