diff options
| author | Thiago Macieira <thiago.macieira@intel.com> | 2025-08-06 18:53:52 -0700 |
|---|---|---|
| committer | Edward Welbourne <edward.welbourne@qt.io> | 2025-08-08 20:39:20 +0000 |
| commit | 76c0d3b9eb1456408cb2dba213f588d75b458057 (patch) | |
| tree | 81e6256d769a62a4f7bee2e257d2951848febe8e /src/testlib/qtestcase.cpp | |
| parent | 799608699350fef4f05632191783f969cfc62606 (diff) | |
QCOMPARE: print the payload of NaNs
Even though QCOMPARE compares them as equal, it may be useful to know
what the payload was when comparing to something else. Plus, there are
other uses of QTest::toString() where the payload may be relevant (e.g.,
in the comparisons of QCborValue).
IEEE 754:2019 5.1.2 "External character sequences representing zeros,
infinities, and NaNs" recommends:
> Conversion of a signaling NaN in a supported format to an external
> character sequence should produce a language-defined one of “snan” or
> “nan” or a sequence that is equivalent except for case, with an
> optional preceding sign.
There's no recommendation about how to represent the payload. It just
says "Language standards should provide [...] a suffix that can
represent the payload", but doesn't say what. I'm using parentheses
because that's what GDB does:
(gdb) p snan
$1 = nan(0x4000000000000)
(gdb) p nan
$2 = nan(0x8000000000000)
Note how it prints the is_quiet bit, but this commit does not.
Change-Id: I02bf59be578d389db535fffd9d392f5efec72754
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/testlib/qtestcase.cpp')
| -rw-r--r-- | src/testlib/qtestcase.cpp | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 72bba52db3d..d8db979af86 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -3085,6 +3085,25 @@ TO_STRING_IMPL(bool, %d) TO_STRING_IMPL(signed char, %hhd) TO_STRING_IMPL(unsigned char, %hhu) +// Merge of ISO C23 getpayload() and issignaling() +template <typename T> static auto decodeNanPayload(T t) +{ + constexpr int Digits = std::numeric_limits<T>::digits; + constexpr quint64 MantissaMask = (Q_UINT64_C(1) << (Digits - 1)) - 1; + constexpr quint64 IsQuietBit = quint64(QT_CONFIG(signaling_nan)) << (Digits - 2); + constexpr quint64 PayloadMask = MantissaMask & ~IsQuietBit; + + struct R { + quint64 payload; + bool isQuiet; + } r; + Q_ASSERT(qIsNaN(t)); + quint64 u = qFromUnaligned<typename QIntegerForSizeof<T>::Unsigned>(&t); + r.payload = u & PayloadMask; + r.isQuiet = !QT_CONFIG(signaling_nan) || (u & IsQuietBit); + return r; +} + static bool signbit(qfloat16 f) { return f.signBit(); } // Be consistent about display of infinities and NaNs (snprintf()'s varies, @@ -3101,7 +3120,13 @@ template <typename T> static char *toStringFp(T t) qstrncpy(msg, (negative ? "-inf" : "inf"), 128); break; case FP_NAN: - qstrncpy(msg, (negative ? "-nan" : "nan"), 128); + if (auto r = decodeNanPayload(t); r.payload) { + std::snprintf(msg, 128, "%s%snan(%#llx)", + negative ? "-" : "", r.isQuiet ? "" : "s", r.payload); + } else { + Q_ASSERT(r.isQuiet); // only quiet NaNs can have payload == 0 + qstrncpy(msg, (negative ? "-nan" : "nan"), 128); + } break; case FP_ZERO: qstrncpy(msg, (negative ? "-0 (-0x0p+0)" : "0 (0x0p+0)"), 128); |
