Search code examples
c++googletest

GoogleTest: Increase precision of EXPECT_EQ float failures


I'm seeing failures of an EXPECT_EQ for 32-bit float comparisons that look very odd:

RawDataConverter_Test.cpp:325: Failure
Expected equality of these values:
  expectedResult
Which is: 0.0726339
  actualResult
Which is: 0.0726339

I know that floating point computations performed two different ways that "mathematically ought to produce the same results" often don't produce exactly the same results due to rounding of intermediate terms (which is why GoogleTest recommends using EXPECT_FLOAT_EQ, EXPECT_DOUBLE_EQ, or EXPECT_NEAR for floating point tests), but that's not my issue here. Both of my "ways of computing" the result are deterministically identical, so they should always produce the same result; if they don't, then someone may have bungled a code change in the production version of the code.

The numbers above look the same to me, so it seemed odd that the EXPECT_EQ would fail. Then I added my own printf of the values before the EXPECT_EQ:

        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);

...and now I see this:

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

Now it's clear why the EXPECT_EQ failed, but it would be nice if GoogleTest's EXPECT_EQ failure output for float comparisons showed sufficient precision in the first place.

I know you can customize GoogleTest failure output to print custom strings for your own classes by providing a PrintTo() function or an overload of std::ostream& operator<<(std::ostream&, const T&), but is there a way to provide a custom PrintTo() or operator<<() for builtin types like float? Or is there any other way to make GoogleTest print float with greater precision than normal?

I tried doing that...

namespace testing
{
   void PrintTo(const float value, std::ostream* os)
   {
      std::streamsize oldPrecision = os->precision();
      os->precision(12);
      *os << value;
      os->precision(oldPrecision);
   }
}

...(with and without the namespace testing {}), but it was never called. I also tried...

// (same error if 2nd argument is ------>  const float &value)
std::ostream& operator<<(std::ostream& os, const float value)
{
   std::streamsize oldPrecision = os.precision();
   os.precision(12);
   os << value;   // <------- error: ambiguous overload for 'operator<<'
   os.precision(oldPrecision);
   return os;
}

...but that doesn't compile for the error reason shown in the comment.

BTW I am using GoogleTest release 1.10.0, released 03 October 2019.


Solution

  • I can think of several ways to get to what you want or to something close to what you want:

    1. Use EXPECT_TRUE with predicate.
    2. Use EXPECT_PRED_FORMAT with predicate (this one can produce your exact output)
    3. Print extra info with EXPECT_EQ, not exact, but close.
    4. Create a custom matcher, again, not exact, but close.

    Here is the code:

    #include <sstream>
    #include <string>
    
    #include "gmock/gmock.h"
    #include "gtest/gtest.h"
    
    //-----------------------------------------------------------------------------
    // Use EXPECT_TRUE With Predicate
    //-----------------------------------------------------------------------------
    testing::AssertionResult MyFloatEq(float m, float n) {
      if (m == n) return testing::AssertionSuccess();
    
      return testing::AssertionFailure() << "because " << m << " != " << n;
    }
    TEST(FloatComparison, UsePred) {
      float expectedResult = 0.07263389974832534800;
      float actualResult = 0.07263390719890594500;
    
      EXPECT_TRUE(MyFloatEq(expectedResult, actualResult));
      std::cout << "----------------------------------------------------"
                << std::endl;
    }
    //-----------------------------------------------------------------------------
    // Use EXPECT_PRED_FORMAT*
    //-----------------------------------------------------------------------------
    testing::AssertionResult MyFloatEq2(const char* m_expr, const char* n_expr,
                                        float m, float n) {
      if (m == n) return testing::AssertionSuccess();
    
      return testing::AssertionFailure()
             << "Expected equality of these values:\n"
             << "  " << m_expr << "\n    Which is: " << m << "\n"
             << "  " << n_expr << "\n    Which is: " << n << "\n";
    }
    TEST(FloatComparison, UsePredFormat) {
      float expectedResult = 0.07263389974832534800;
      float actualResult = 0.07263390719890594500;
    
      EXPECT_PRED_FORMAT2(MyFloatEq2, expectedResult, actualResult);
      std::cout << "----------------------------------------------------"
                << std::endl;
    }
    //-----------------------------------------------------------------------------
    // Print extra info
    //-----------------------------------------------------------------------------
    std::string DisplayFloatEq(const float expectedResult,
                               const float actualResult) {
      std::stringstream ss;
      ss << "( displayed with high precision: " << std::setprecision(12)
         << expectedResult << ", " << actualResult << " )";
    
      return ss.str();
    }
    
    TEST(FloatComparison, PrintExtraInfo) {
      float expectedResult = 0.07263389974832534800;
      float actualResult = 0.07263390719890594500;
    
      EXPECT_EQ(expectedResult, actualResult)
          << DisplayFloatEq(expectedResult, actualResult);
    
      std::cout << "----------------------------------------------------"
                << std::endl;
    }
    //-----------------------------------------------------------------------------
    // Use a Custom Matcher
    //-----------------------------------------------------------------------------
    MATCHER_P(IsFloatEq, a,
              (std::string(negation ? "should not be equal to "
                                    : "should be equal to ") +
               (std::stringstream() << std::setprecision(12) << a).str())) {
      *result_listener << "( displayed with high precision: "
                       << std::setprecision(12) << arg << " )";
    
      return a == arg;
    }
    
    TEST(FloatComparison, UseCustomMatcher) {
      float expectedResult = 0.07263389974832534800;
      float actualResult = 0.07263390719890594500;
    
      EXPECT_THAT(expectedResult, IsFloatEq(actualResult));
      std::cout << "----------------------------------------------------"
                << std::endl;
    }
    

    Live example: https://godbolt.org/z/GhT3EaP1c

    Output:

    Gtest main is printing[==========] Running 4 tests from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 4 tests from FloatComparison
    [ RUN      ] FloatComparison.UsePred
    example.cpp:19: Failure
    Value of: MyFloatEq(expectedResult, actualResult)
      Actual: false (because 0.072633899748325348 != 0.072633907198905945)
    Expected: true
    ----------------------------------------------------
    [  FAILED  ] FloatComparison.UsePred (0 ms)
    [ RUN      ] FloatComparison.UsePredFormat
    example.cpp:39: Failure
    Expected equality of these values:
      expectedResult
        Which is: 0.072633899748325348
      actualResult
        Which is: 0.072633907198905945
    
    ----------------------------------------------------
    [  FAILED  ] FloatComparison.UsePredFormat (0 ms)
    [ RUN      ] FloatComparison.PrintExtraInfo
    example.cpp:59: Failure
    Expected equality of these values:
      expectedResult
        Which is: 0.0726339
      actualResult
        Which is: 0.0726339
    ( displayed with high precision: 0.0726338997483, 0.0726339071989 )
    ----------------------------------------------------
    [  FAILED  ] FloatComparison.PrintExtraInfo (0 ms)
    [ RUN      ] FloatComparison.UseCustomMatcher
    example.cpp:82: Failure
    Value of: expectedResult
    Expected: should be equal to 0.0726339071989
      Actual: 0.0726339 (of type float), ( displayed with high precision: 0.0726338997483 )
    ----------------------------------------------------
    [  FAILED  ] FloatComparison.UseCustomMatcher (0 ms)