Gtest在零附近的C++浮点比较失败
C++ Float Comparison Around Zero Fails With Gtest
我们在我的一个单元测试中挣扎了很长一段时间。在调查过程中,我们发现了根本原因,这似乎是浮动中的比较(请参阅下面的代码片段,我简化了计算,但它仍然失败)。
TEST_F( MyFloatTest, thisOneDoesFail)
{
const float toCompare = 0.2f - 1.0f + 0.9f;
EXPECT_FLOAT_EQ( toCompare, 0.1f );
}
结果是:
实际值:0.1应为:to比较即:0.099999964
有一些数字数学背景,我们仍然不知道为什么这个测试失败,而使用std::numerical_limits:epsilon的自定义浮点比较通过了。所以在某个时候,我们开始认为GTest是错误的,我们对它进行了调试。它使用了奇怪的表达式,我们没有完全理解。更奇怪的是:下面的测试通过了,尽管我只添加了一个1:
TEST_F( MyFloatTest, thisOnePasses)
{
const float toCompare = 1.2f - 1.0f + 0.9f;
EXPECT_FLOAT_EQ( toCompare, 1.1f );
}
我们认为在包含负浮点值时可能会出现一些问题,但下一个测试也通过了:
TEST_F( MyFloatTest, thisOnePassesAlso)
{
const float toCompare = 0.2f - 1.0f + 1.9f;
EXPECT_FLOAT_EQ( toCompare, 1.1f );
}
因此,对我们来说,Gtest的EXPECT_FLOAT_EQ宏似乎只是在零附近存在问题。有人知道这种行为吗?你在你的环境中见过类似的情况吗?(顺便说一句:我们使用的是MSVC2015)它只是因为GTest中提到的4 ULP精度而意外失败吗?(我们也不完全清楚)。
问题是,具有较小值和较大中间值的浮点和往往会有较大的相对误差。您可以通过编写来减少错误
const float toCompare = 0.2f - (1.0f - 0.9f);
在原始代码中,最大的中间值是0.2-1.0=-0.8,是最终结果的八倍。更改代码后,最大中间值为0.1,等于最终结果。如果你检查通过的示例测试,在每种情况下,你都没有比最终结果更大的中间结果。
问题不在于EXPECT_FLOAT_EQ宏,而在于计算。
它只是由于GTest中提到的4 ULP精度而意外失败吗?
在我看来就是这样。
尝试以下(非常粗糙,不可移植!)测试代码:
float toCompare = 0.2f - 1.0f + 0.9f;
int i = *reinterpret_cast<int*>(&toCompare);
std::cout << i << 'n';
float expected = 0.1f;
i = *reinterpret_cast<int*>(&expected);
std::cout << i << 'n';
在我的系统上,输出是:
1036831944
1036831949
尾数正好相隔5个ULP。4 ULP的比较对于计算的误差是不够的。
0.2f - 1.0f
很好,在我的系统上根本没有精度错误。剩下的是-0.8f + 0.9f
。这就是错误的来源(在我的系统上)。我不是一个足够的专家来告诉你为什么这个计算有5 ULP精度误差。
在预期出现一定程度错误的情况下,请使用EXPECT_NEAR
。
在我看来,问题是你假设0.2f-1.0f+0.9f等于0.1f。0.2 0.9 0.1中没有一个可以精确地表示为浮点(或双精度或任何其他二进制浮点表示)。
0.2f和0.9f实际上是0.2和0.9的近似值,几乎没有理由假设你的和会给出与0.1f相同的0.1近似值。虽然0.2f和0.9的相对误差大致相同,但由于抵消,和的相对误差可能会大得多。
如果你对可以精确表示为浮点的数字做同样的事情,例如0.25f-1.0f+0.875f,你会发现这等于0.125f
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- C++ TCP 服务器比较字符串失败
- SuperLu和LaPack的比较在与犰狳进行基准测试时失败
- 标准::元组成员逐个成员比较失败
- C 拖拖与原始角色的比较失败
- 假定相同的字符串之间的比较失败' == '比较
- 断言在与 opencv 的像素比较中失败
- Gtest在零附近的C++浮点比较失败
- 使用比较函数对引用类型失败
- 从txt文件中读取行后字符串比较失败
- 使用下标运算符进行Integer Vector元素比较失败,但使用at函数成功
- 矢量比较失败
- 字符串比较失败-C++
- 逻辑比较在不应该C++时失败
- 比较测试失败:unorder_map上的[]操作符产生的参数数量错误
- 字符串比较在c++中失败
- 比较具有相同持续时间的两个std::chrono::time_points失败
- 为什么我在double和c++之间的比较失败
- 字符串==字符串比较失败的原因
- 为什么这个迭代器比较失败