GoogleTest中EXPECT_EQ比较float时如何提升失败输出精度
问题表现
使用EXPECT_EQ对32位float类型值做相等比较时遇到反常失败,失败日志如下:
RawDataConverter_Test.cpp:325: Failure Expected equality of these values: expectedResult Which is: 0.0726339 actualResult Which is: 0.0726339
已知浮点计算中,两种数学上结果相同的计算路径往往会因为中间值舍入产生微小差异,这也是GoogleTest官方推荐浮点测试使用EXPECT_FLOAT_EQ、EXPECT_DOUBLE_EQ或EXPECT_NEAR断言的原因,但该方案不适用于当前场景:本次对比的两个结果计算逻辑是确定性完全一致的,正常情况下必然输出完全相同的结果,如果结果不一致,就代表生产代码被错误修改。
上述日志中两个值显示完全相同,但EXPECT_EQ断言失败,现象反常。因此在EXPECT_EQ调用前添加自定义printf打印两个值的高精度表示和十六进制内存值,代码如下:
float expectedResult = ... float actualResult = ... printf("\nexp=%0.20f (0x%08x)\nact=%0.20f (0x%08x) (%s)\n", expectedResult, expectedResultU32, actualResult, actualResultU32, (expectedResult == actualResult) ? "same" : "diff"); EXPECT_EQ(expectedResult, actualResult);
打印结果如下:
exp=0.07263389974832534800 (0x3d94c115) act=0.07263390719890594500 (0x3d94c116) (diff) RawDataConverter_Test.cpp:325: Failure Expected equality of these values: expectedResult Which is: 0.0726339 actualResult Which is: 0.0726339
从打印结果可以确认两个float实际存在1个ULP的差异,只是GoogleTest默认打印精度不足,导致日志中两个值看起来完全一致。
核心诉求
希望GoogleTest在输出float类型的EXPECT_EQ失败日志时,默认就能打印足够精度的数值,直接展示出值的实际差异。
已知可以通过为自定义类提供PrintTo()函数或重载std::ostream& operator<<(std::ostream&, const T&)的方式自定义GoogleTest的失败输出内容,但存在以下疑问:
- 是否可以为
float这类内置类型提供自定义PrintTo()或operator<<()? - 是否有其他方式可以让GoogleTest以高于默认的精度打印
float类型值?
已尝试的无效方案
自定义testing命名空间下的PrintTo函数
实现代码如下:
namespace testing { void PrintTo(const float value, std::ostream* os) { std::streamsize oldPrecision = os->precision(); os->precision(12); *os << value; os->precision(oldPrecision); } }
测试结果:无论是否将代码放在namespace testing {}命名空间内,该函数都不会被调用。
重载float类型的输出运算符
实现代码如下:
// (第二个参数写为const float &value时会报相同错误) std::ostream& operator<<(std::ostream& os, const float value) { std::streamsize oldPrecision = os.precision(); os.precision(12); os << value; // <------- 错误: 'operator<<' 重载歧义 os.precision(oldPrecision); return os; }
测试结果:代码无法编译,错误原因如注释所示。
补充说明:当前使用的是2019年10月3日发布的GoogleTest 1.10.0版本。
内容的提问来源于stack exchange,提问作者phonetagger

