summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qnumeric.h10
-rw-r--r--tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp46
2 files changed, 56 insertions, 0 deletions
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 5a25b1c2392..2e13da4b850 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -336,6 +336,16 @@ template <auto V2, typename T> bool qMulOverflow(T v1, T *r)
template <typename T>
constexpr inline T qAbs(const T &t) { return t >= 0 ? t : -t; }
+namespace QtPrivate {
+template <typename T,
+ typename std::enable_if_t<std::is_integral_v<T>, bool> = true>
+constexpr inline auto qUnsignedAbs(T t)
+{
+ using U = std::make_unsigned_t<T>;
+ return (t >= 0) ? U(t) : U(~U(t) + U(1));
+}
+} // namespace QtPrivate
+
// gcc < 10 doesn't have __has_builtin
#if defined(Q_PROCESSOR_ARM_64) && (__has_builtin(__builtin_round) || defined(Q_CC_GNU)) && !defined(Q_CC_CLANG)
// ARM64 has a single instruction that can do C++ rounding with conversion to integer.
diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
index 37127b4ce09..ec7896ca4fc 100644
--- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
+++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
@@ -9,6 +9,7 @@
#include <math.h>
#include <float.h>
+#include <limits.h>
#include <QtCore/q26numeric.h>
@@ -789,5 +790,50 @@ static_assert(q26::saturate_cast<quint64>(min<qint64>) == 0);
} // namespace SaturateCastTest
+namespace UnsignedAbsTest {
+using QtPrivate::qUnsignedAbs;
+
+static_assert(std::is_same_v<decltype(qUnsignedAbs((char)0)), unsigned char>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((signed char)0)), unsigned char>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((unsigned char)0)), unsigned char>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((short)0)), unsigned short>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((unsigned short)0)), unsigned short>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((int)0)), unsigned int>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((unsigned int)0)), unsigned int>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((long)0)), unsigned long>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((unsigned long)0)), unsigned long>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((long long)0)), unsigned long long>);
+static_assert(std::is_same_v<decltype(qUnsignedAbs((unsigned long long)0)), unsigned long long>);
+
+template <typename T> constexpr bool isEqual(T a, T b) { return a == b; }
+
+#define TEST_TYPE(type, utype, minimal, maximal) \
+ static_assert(isEqual(qUnsignedAbs(type(minimal)), (utype)((utype)(maximal) + (utype)1))); \
+ static_assert(isEqual(qUnsignedAbs(type(0)), (utype)(0))); \
+ static_assert(isEqual(qUnsignedAbs(type(1)), (utype)(1))); \
+ static_assert(isEqual(qUnsignedAbs(type(-1)), (utype)(1))); \
+ static_assert(isEqual(qUnsignedAbs(type(10)), (utype)(10))); \
+ static_assert(isEqual(qUnsignedAbs(type(-10)), (utype)(10))); \
+ static_assert(isEqual(qUnsignedAbs(type(maximal)), (utype)(maximal))); \
+
+using schar = signed char;
+using uchar = unsigned char;
+using ushort = unsigned short;
+using uint = unsigned int;
+using ulong = unsigned long;
+
+#if CHAR_MAX == 127
+// char is signed
+TEST_TYPE(char, uchar, CHAR_MIN, CHAR_MAX)
+#endif
+TEST_TYPE(schar, uchar, SCHAR_MIN, SCHAR_MAX)
+TEST_TYPE(short, ushort, SHRT_MIN, SHRT_MAX)
+TEST_TYPE(int, uint, INT_MIN, INT_MAX)
+TEST_TYPE(long, ulong, LONG_MIN, LONG_MAX)
+TEST_TYPE(qlonglong, qulonglong, LLONG_MIN, LLONG_MAX)
+
+#undef TEST_TYPE
+} // namespace UnsignedAbsTest
+
QTEST_APPLESS_MAIN(tst_QNumeric)
#include "tst_qnumeric.moc"