summaryrefslogtreecommitdiffstats
path: root/src/testlib/qtestcase.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2025-08-06 18:53:52 -0700
committerEdward Welbourne <edward.welbourne@qt.io>2025-08-08 20:39:20 +0000
commit76c0d3b9eb1456408cb2dba213f588d75b458057 (patch)
tree81e6256d769a62a4f7bee2e257d2951848febe8e /src/testlib/qtestcase.cpp
parent799608699350fef4f05632191783f969cfc62606 (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.cpp27
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);