diff options
-rw-r--r-- | src/corelib/global/qnumeric_p.h | 93 | ||||
-rw-r--r-- | tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp | 31 |
2 files changed, 123 insertions, 1 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index 650c32d44b..8ff4ffc7a7 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -161,7 +161,7 @@ static inline bool qt_is_finite(float f) } // -// Overflow math +// Unsigned overflow math // namespace { template <typename T> inline typename QtPrivate::QEnableIf<QtPrivate::is_unsigned<T>::value, bool>::Type @@ -262,6 +262,97 @@ template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigne #else # undef HAVE_MUL64_OVERFLOW #endif + +// +// Signed overflow math +// +// In C++, signed overflow math is Undefined Behavior. However, many CPUs do implement some way to +// check for overflow. Some compilers expose intrinsics to use this functionality. If the no +// intrinsic is exposed, overflow checking can be done by widening the result type and "manually" +// checking for overflow. Or, alternatively, by using inline assembly to use the CPU features. +// +// Only int overflow checking is implemented, because it's the only one used. +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_sadd_overflow) +inline bool add_overflow(int v1, int v2, int *r) +{ return __builtin_sadd_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool add_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool add_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) + v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_ssub_overflow) +inline bool sub_overflow(int v1, int v2, int *r) +{ return __builtin_ssub_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool sub_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool sub_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) - v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_smul_overflow) +inline bool mul_overflow(int v1, int v2, int *r) +{ return __builtin_smul_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool mul_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("imul %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool mul_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) * v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + } QT_END_NAMESPACE diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index 01ff3a6c7e..1847056de5 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -55,6 +55,7 @@ private slots: void addOverflow(); void mulOverflow_data(); void mulOverflow(); + void signedOverflow(); }; void tst_QNumeric::fuzzyCompare_data() @@ -378,5 +379,35 @@ void tst_QNumeric::mulOverflow() MulOverflowDispatch<quint64>()(); } +void tst_QNumeric::signedOverflow() +{ + const int minInt = std::numeric_limits<int>::min(); + const int maxInt = std::numeric_limits<int>::max(); + int r; + + QCOMPARE(add_overflow(minInt + 1, int(-1), &r), false); + QCOMPARE(add_overflow(minInt, int(-1), &r), true); + QCOMPARE(add_overflow(minInt, minInt, &r), true); + QCOMPARE(add_overflow(maxInt - 1, int(1), &r), false); + QCOMPARE(add_overflow(maxInt, int(1), &r), true); + QCOMPARE(add_overflow(maxInt, maxInt, &r), true); + + QCOMPARE(sub_overflow(minInt + 1, int(1), &r), false); + QCOMPARE(sub_overflow(minInt, int(1), &r), true); + QCOMPARE(sub_overflow(minInt, maxInt, &r), true); + QCOMPARE(sub_overflow(maxInt - 1, int(-1), &r), false); + QCOMPARE(sub_overflow(maxInt, int(-1), &r), true); + QCOMPARE(sub_overflow(maxInt, minInt, &r), true); + + QCOMPARE(mul_overflow(minInt, int(1), &r), false); + QCOMPARE(mul_overflow(minInt, int(-1), &r), true); + QCOMPARE(mul_overflow(minInt, int(2), &r), true); + QCOMPARE(mul_overflow(minInt, minInt, &r), true); + QCOMPARE(mul_overflow(maxInt, int(1), &r), false); + QCOMPARE(mul_overflow(maxInt, int(-1), &r), false); + QCOMPARE(mul_overflow(maxInt, int(2), &r), true); + QCOMPARE(mul_overflow(maxInt, maxInt, &r), true); +} + QTEST_APPLESS_MAIN(tst_QNumeric) #include "tst_qnumeric.moc" |