C ++浮点数比较零失败与Gtest

时间:2016-05-31 08:38:54

标签: c++ floating-point googletest numerical

我们正在努力进行一次单位测试。在调查期间,我们找到了根本原因,这似乎是浮点数的比较(请参阅下面的代码片段,其中我简化了计算,但仍然失败)。

TEST_F( MyFloatTest, thisOneDoesFail)
{
    const float toCompare = 0.2f - 1.0f + 0.9f;
    EXPECT_FLOAT_EQ( toCompare, 0.1f );
}

结果是:

  

实际: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 );
}

我们认为在包含负浮点值时可能会出现问题,但下一个测试也会通过:

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精度,它是否偶然失败? (这对我们来说也不是很清楚)。

3 个答案:

答案 0 :(得分:5)

  

由于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

mantissae正好相隔5个ULP。 4 ULP比较不足以计算误差。

0.2f - 1.0f没问题,我的系统根本没有准确性错误。你留下的是-0.8f + 0.9f。这是错误来自(在我的系统上)。我不是一个足够专家告诉你为什么这个计算有5个ULP精度误差。

如果预计会出现某种程度的错误,请使用EXPECT_NEAR

答案 1 :(得分:4)

问题是具有较小值和较大中间值的浮点和将倾向于具有较大的相对误差。您可以通过编写

来减少错误
const float toCompare = 0.2f - (1.0f - 0.9f);

在原始代码中,最大中间值为0.2 - 1.0 = -0.8,比最终结果大8倍。使用更改的代码,最大中间值为0.1,等于最终结果。如果检查通过的示例测试,则在每种情况下都没有与最终结果相比较大的中间结果。

问题不在于EXPECT_FLOAT_EQ宏,而在于计算。

答案 2 :(得分:0)

我认为问题在于你假设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.9f中的相对误差都将大致相同,但由于取消,总和中的相对误差可能会大得多。

如果您使用可以精确表示为浮点数的数字尝试相同的事情,例如0.25f - 1.0f + 0.875f,您会发现这等于0.125f