比较浮点数 - Google Test Framework

Raj*_*war 3 c++ floating-point floating-accuracy

虽然用户@skrebbel在SO上发表了这篇文章,但他表示谷歌测试框架在比较浮动和双打方面做得很好而且快速.所以我编写了下面的代码来检查代码的有效性,显然我似乎在这里遗漏了一些东西,因为我希望在这里输入几乎相等的部分这是我的代码

float left = 0.1234567;
float right= 0.1234566;

const FloatingPoint<float> lhs(left), rhs(right);


if (lhs.AlmostEquals(rhs)) 
{
    std::cout << "EQUAL"; //Shouldnt it have entered here ?
}
Run Code Online (Sandbox Code Playgroud)

任何建议都会受到赞赏.

Eri*_*hil 8

leftright他们之间并没有"几乎相等",因为它们相距太远,远远超过默认容差AlmostEquals.您链接的问题中的一个答案中的代码显示容差为4 ULP,但您的数字相差14 ULP(使用IEEE 754 32位二进制和正确舍入软件).(ULP是浮点值的最小增量.对于小幅度的浮点数而言,它是小的,对于大数字,它是大的,因此它大约相对于数字的大小.)

如果不了解您要比较的值中的错误以及您正在执行的比较,则不应执行任何浮点比较.

人们经常错误地认为你无法测试浮点值是否相等.这是假的; 执行a == b是一个完美的操作.当且仅当它返回true a等于b(即,ab是数字具有完全相同的值).实际问题是他们试图在给定错误输入的情况下计算正确的函数.==是一个函数:它需要两个输入并返回一个值.显然,如果你给任何功能不正确的输入,可能会返回错误的结果.所以这里的问题不是浮点比较; 这是不正确的输入.在给定错误输入的情况下,通常无法正确计算总和,乘积,平方根,对数或任何其他函数.因此,在使用浮点时,必须设计一个算法来处理近似值(或者,在特殊情况下,要特别小心,以确保不会引入错误).

通常人们会尝试通过接受略有不同的相同数字来解决其浮点值中的错误.这减少了假阴性(由于先前的计算错误导致的不平等的指示),代价是增加误报(由不接受引起的平等的指示).这种错误与另一种错误的交换是否可接受取决于应用程序.没有通用的解决方案,这就是为什么类似AlmostEquals的功能通常是坏的.

浮点值中的错误是先前操作和值的结果.这些误差的范围可以从零到无穷大,具体取决于具体情况.因此,不应该简单地接受函数的默认容差,例如AlmostEquals.相反,应该计算容差,该容差特定于它们的应用,需求和计算,并使用该计算的容差(或者根本不使用比较).

另一个问题是诸如AlmostEquals通常使用相对于被比较的值指定的容差来编写的函数.但是,值中的误差可能受到幅度差别很大的中间值的影响,因此最终误差可能是所比较的值中不存在的数据的函数.

在测试其他代码的代码中,"近似"浮点比较可能是可以接受的,因为大多数错误都可能导致大错误,因此对等同性的宽松接受将允许良好的代码继续,但会报告大多数错误代码中的错误.但是,即使在这种情况下,也必须适当地设置预期结果和允许的容错.该AlmostEquals代码出现硬编码的容错.

  • @Michael:容忍度不是由类型决定的。例如,参见第 5 节,JM。Muller 的递归,在 Kahan 的[浮点计算中对舍入的无意识评估有多徒劳?](http://www.cs.berkeley.edu/~wkahan/Mindless.pdf)。本质上,无论类型如何,此递归的计算都会收敛到 100。数学上正确的极限是 5。误差为 95 **无论精度如何**。要计算公差,您**必须**具有完整的上下文,包括先前计算和数据的知识。(William Kahan 是我们拥有 IEEE 754 的原因。这篇论文值得一读。) (2认同)

pio*_*kuc 7

您可以使用

ASSERT_NEAR(val1, val2, abs_error); 
Run Code Online (Sandbox Code Playgroud)

您可以在哪里给出可接受的 - 您选择的,例如0.0000001 - 差异abs_error,如果默认值太小,请参阅https://github.com/google/googletest/blob/master/googletest/docs/advanced .MD#浮点比较