Mat*_*ttW 6 c++ floating-point numerical googletest
我们正在努力进行一次单位测试.在调查期间,我们找到了根本原因,这似乎是浮点数的比较(请参阅下面的代码片段,其中我简化了计算,但仍然失败).
TEST_F( MyFloatTest, thisOneDoesFail)
{
const float toCompare = 0.2f - 1.0f + 0.9f;
EXPECT_FLOAT_EQ( toCompare, 0.1f );
}
Run Code Online (Sandbox Code Playgroud)
结果是:
实际:0.1预期:比较哪个是: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 );
}
Run Code Online (Sandbox Code Playgroud)
我们认为在包含负浮点值时可能会出现问题,但下一个测试也会通过:
TEST_F( MyFloatTest, thisOnePassesAlso)
{
const float toCompare = 0.2f - 1.0f + 1.9f;
EXPECT_FLOAT_EQ( toCompare, 1.1f );
}
Run Code Online (Sandbox Code Playgroud)
所以对我们来说,看起来Gtest的EXPECT_FLOAT_EQ宏确实只是零问题.有谁知道这种行为?你有没有在你的环境中看到类似的东西?(顺便说一下:我们使用的是MSVC2015)由于GTest中提到的4 ULP精度,它是否偶然失败?(这对我们来说也不是很清楚).
问题在于,具有较小值和较大中间值的浮点和往往会产生较大的相对误差。你可以通过写来减少错误
const float toCompare = 0.2f - (1.0f - 0.9f);
Run Code Online (Sandbox Code Playgroud)
在原始代码中,最大中间值为 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';
Run Code Online (Sandbox Code Playgroud)
在我的系统上输出是:
1036831944
1036831949
Run Code Online (Sandbox Code Playgroud)
mantissae正好相隔5个ULP.4 ULP比较不足以计算误差.
0.2f - 1.0f很好,我的系统根本没有准确性错误.你剩下的就是-0.8f + 0.9f.这是错误来自(在我的系统上).我不是一个足够专家告诉你为什么这个计算有5个ULP精度误差.
如果预计会出现一定程度的错误,请使用EXPECT_NEAR.