From 9c332145b71c36a5bad9688312c79184f98601ff Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 16 Nov 2022 09:17:58 -0800 Subject: [PATCH] When printing floating-point numbers, print full precision by default. To make debug output readable, we still use the faster 6-digit precision sometimes, but only if it will round-trip. This way, when a test fails due to a very small difference in floating-point numbers, users will have enough digits to see the difference. PiperOrigin-RevId: 488958311 Change-Id: Ibcac43f48a97006d89217530c69386cc4fa2735c --- googletest/include/gtest/gtest-printers.h | 75 +++++++++++++++++++++ googletest/test/googletest-printers-test.cc | 10 ++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h index 89215e620f..eeaef04e06 100644 --- a/googletest/include/gtest/gtest-printers.h +++ b/googletest/include/gtest/gtest-printers.h @@ -484,6 +484,81 @@ GTEST_API_ void PrintTo(__uint128_t v, ::std::ostream* os); GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); #endif // __SIZEOF_INT128__ +// The default resolution used to print floating-point values uses only +// 6 digits, which can be confusing if a test compares two values whose +// difference lies in the 7th digit. So we'd like to print out numbers +// in full precision. +// However if the value is something simple like 1.1, full will print a +// long string like 1.100000001 due to floating-point numbers not using +// a base of 10. This routiune returns an appropriate resolution for a +// given floating-point number, that is, 6 if it will be accurate, or a +// max_digits10 value (full precision) if it won't, for values between +// 0.0001 and one million. +// It does this by computing what those digits would be (by multiplying +// by an appropriate power of 10), then dividing by that power again to +// see if gets the original value back. +// A similar algorithm applies for values larger than one million; note +// that for those values, we must divide to get a six-digit number, and +// then multiply to possibly get the original value again. +template +int AppropriateResolution(FloatType val) { + int full = std::numeric_limits::max_digits10; + if (val < 0) val = -val; + + if (val < 1000000) { + FloatType mulfor6 = 1e10; + if (val >= 100000.0) { // 100,000 to 999,999 + mulfor6 = 1.0; + } else if (val >= 10000.0) { + mulfor6 = 1e1; + } else if (val >= 1000.0) { + mulfor6 = 1e2; + } else if (val >= 100.0) { + mulfor6 = 1e3; + } else if (val >= 10.0) { + mulfor6 = 1e4; + } else if (val >= 1.0) { + mulfor6 = 1e5; + } else if (val >= 0.1) { + mulfor6 = 1e6; + } else if (val >= 0.01) { + mulfor6 = 1e7; + } else if (val >= 0.001) { + mulfor6 = 1e8; + } else if (val >= 0.0001) { + mulfor6 = 1e9; + } + if (static_cast(val * mulfor6 + 0.5) / mulfor6 == val) return 6; + } else if (val < 1e10) { + FloatType divfor6 = 1.0; + if (val >= 1e9) { // 1,000,000,000 to 9,999,999,999 + divfor6 = 10000; + } else if (val >= 1e8) { // 100,000,000 to 999,999,999 + divfor6 = 1000; + } else if (val >= 1e7) { // 10,000,000 to 99,999,999 + divfor6 = 100; + } else if (val >= 1e6) { // 1,000,000 to 9,999,999 + divfor6 = 10; + } + if (static_cast(val / divfor6 + 0.5) * divfor6 == val) return 6; + } + return full; +} + +inline void PrintTo(float f, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(f)); + *os << f; + os->precision(old_precision); +} + +inline void PrintTo(double d, ::std::ostream* os) { + auto old_precision = os->precision(); + os->precision(AppropriateResolution(d)); + *os << d; + os->precision(old_precision); +} + // Overloads for C strings. GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); inline void PrintTo(char* s, ::std::ostream* os) { diff --git a/googletest/test/googletest-printers-test.cc b/googletest/test/googletest-printers-test.cc index d2d88a5220..8a7db25b0c 100644 --- a/googletest/test/googletest-printers-test.cc +++ b/googletest/test/googletest-printers-test.cc @@ -458,7 +458,15 @@ TEST(PrintBuiltInTypeTest, Int128) { // Floating-points. TEST(PrintBuiltInTypeTest, FloatingPoints) { - EXPECT_EQ("1.5", Print(1.5f)); // float + // float (32-bit precision) + EXPECT_EQ("1.5", Print(1.5f)); + + EXPECT_EQ("1.0999999", Print(1.09999990f)); + EXPECT_EQ("1.1", Print(1.10000002f)); + EXPECT_EQ("1.10000014", Print(1.10000014f)); + EXPECT_EQ("9e+09", Print(9e9f)); + + // double EXPECT_EQ("-2.5", Print(-2.5)); // double }