使用Catch2打印浮点数的完整精度

4

我有一些使用的测试代码,用于检查某些计算是否返回浮点空值。

CHECK( someFunc() == 0. );

问题在于,当测试因非常小的非空值(比如1.234E-16)而失败时,这些值会以“默认”打印出来,我会看到:
my_test.cpp:351: FAILED:
  CHECK( someFunc() == 0.0 )
with expansion:
  0.0 == 0.0

这几乎是没有用的。我想要看到的是:
my_test.cpp:351: FAILED:
  CHECK( someFunc() == 0.0 )
with expansion:
  1.234E-16 == 0.0

我在测试前尝试在std :: cout 中流式传输 std :: scientific ,但显然Catch使用另一种打印方法。
有什么想法吗?
附注:实际上,我使用提供的{{link1: Approx }}类,但这与我的问题无关。 编辑:这里的问题不是比较本身(我知道关于浮点值的所有恶魔般的事情),它仅仅是关于如何告诉Catch打印处理的值。

如果您使用了Approx,请展示它。否则,人们可能会抱怨您在浮点值之间使用直接比较的方式是有缺陷的。整个[mcve]的重点应该是展示您的问题,并且不引入其他无关的问题。 - Some programmer dude
我本来会打赌我会看到这个评论的;-) 谢谢,我知道所有与浮点数值相关的问题,但有时候,这种情况就是发生了...而且这种情况(有点)被提供的类处理了(请参见链接)。 - kebs
@Someprogrammerdude,使用 someFunc() { return 0;} 将通过测试,但在某些奇怪的边角情况下,使用 == 可能是可以的,因此这个问题是合理的(当然,我同意给出不使用 == 的提示)。 - 463035818_is_not_a_number
如果您检查例如 15.000000000001 < 15,输出是否会产生相同的效果?也许您可以完全避免浮点数 == 问题,并仍然得出相同的解决方案。 - 463035818_is_not_a_number
这是因为我输入了太多数字 :( - 463035818_is_not_a_number
显示剩余2条评论
1个回答

4
更新:您现在可以 在Catch2中指定精度。以下内容适用于旧版本的Catch2。
看起来精度是硬编码在Catch2本身中的:
std::string StringMaker<float>::convert(float value) {
    return fpToString(value, 5) + 'f';
}
std::string StringMaker<double>::convert(double value) {
    return fpToString(value, 10);
}

有两个解决方案:

方案1:修改Catch2

如果你修改它,就可以让它显示你想要的内容(注意:<limits>已经在catch中包含,因此我将使用std::numeric_limits):

std::string StringMaker<float>::convert(float value) {
    return fpToString(value, std::numeric_limits<float>::max_digits10) + 'f';
}
std::string StringMaker<double>::convert(double value) {
    return fpToString(value, std::numeric_limits<double>::max_digits10);
}

更为复杂的方法是让用户可以设置此参数,而不是将其硬编码为另一个半随意的值,但这只是一个问答,而不是一个拉取请求。;-)

选项2:在更高精度下自行记录日志

如果在REQUIRE()调用之前添加INFO( FullPrecision(d) );,则仅在测试用例失败时才会获得完整精度的打印输出。(请参见下面FullPrecision()的定义。)

这两个更改在此处演示:

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include <limits>
#include <sstream>
#include <iomanip>

double GetDouble() { return std::numeric_limits<double>::epsilon(); }

std::string FullPrecision( double d )
{
    auto s = std::ostringstream{};
    s << std::setprecision( std::numeric_limits<double>::max_digits10 ) << d;
    return s.str();
}

TEST_CASE( "Double, double, toil and trouble", "[double]" ) 
{
    const auto d = GetDouble();
    INFO( FullPrecision(d) );
    REQUIRE( 0.0 == d );
}

打印如下内容:

prog.cc:20: FAILED:
  REQUIRE( 0.0 == d )
with expansion:
  0.0 == 0.00000000000000022
with message:
  2.2204460492503131e-16

修改Catch2会导致扩展0.0 == 0.00000000000000022,添加INFO()会导致消息2.2204460492503131e-16
Wandbox上实时查看。

嘿,谢谢你那非常出色的回答!我会看一下的。 - kebs
接受,第二个解决方案对我来说很好(更新时编辑catch将是一个问题)。奇怪的是,使用您最后的示例,我不明白您发布的内容,我得到的是0.0 == 0.0(但由于INFO打印了正确的值,所以没问题)。 - kebs
1
更新:这现在是Catch2中的一个功能。 - metal
你没有理解我展示的内容:这是因为我的Wandbox示例中包含了两种方法。修改Catch2会导致扩展0.0 == 0.00000000000000022,添加INFO()会导致消息2.2204460492503131e-16。如果你没有修改Catch2,你只会看到后者。 - metal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接