【问题标题】:C++ Float Comparison Around Zero Fails With Gtest使用 Gtest 进行零附近的 C++ 浮点比较失败
【发布时间】:2016-05-31 08:38:53
【问题描述】:

一段时间以来,我们一直在为我的一个单元测试而苦苦挣扎。在调查过程中,我们找到了根本原因,这似乎是浮点数的比较(参见下面的代码 sn-p,我已经简化了计算但仍然失败)。

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 精度而意外失败? (我们也不完全清楚)。

【问题讨论】:

  • This 可能会有所帮助
  • 显示EXPECT_FLOAT_EQ宏怎么样?

标签: c++ floating-point googletest numerical


【解决方案1】:

问题是具有小值和大中间值的浮点和往往会产生较大的相对误差。你通过写来减少错误

const float toCompare = 0.2f - (1.0f - 0.9f);

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

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

【讨论】:

  • 谢谢,添加括号实际上解决了问题,EXPECT_FLOAT_EQ 不再失败!在我的例子中,0.9f 不是固定的(它只是为了简化而添加在这里),所以我会再看一下我的计算,但现在我知道在哪里看。
【解决方案2】:

是否只是由于 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

【讨论】:

    【解决方案3】:

    在我看来,问题在于您假设 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-02
      • 1970-01-01
      相关资源
      最近更新 更多