Update the overflow functions to include qsizetype

Commit 29bc68cf16 added support for
unsigned and commit 5ff7a3d96e later added
support for int. This commit adds support for qsizetype, which isn't int
on 64-bit platforms.

We do this by reorganizing the code and using the generic version of
__builtin_{add,sub,mul}_overflow from GCC 5 and Clang 3.8, which ICC 18
seems to support now too on Linux. That leaves older versions of GCC and
Clang, as well as MSVC, ICC on Windows, and the GHS compiler, to use the
generic implementations, as I've removed the assembly code those
versions of GCC and Clang on x86 are now uncommon.

Note: any older version of ICC probably breaks. We only support the
latest.

Change-Id: I9e2892cb6c374e93bcb7fffd14fc11bcd5f067a7
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
bb10
Thiago Macieira 2017-11-30 20:37:57 -08:00 committed by Gabriel de Dietrich
parent 34e8cafa29
commit 43c44d05ca
2 changed files with 261 additions and 168 deletions

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -58,8 +58,6 @@
#if defined(Q_CC_MSVC)
# include <intrin.h>
#elif defined(Q_CC_INTEL)
# include <immintrin.h> // for _addcarry_u<nn>
#endif
#if defined(Q_CC_MSVC)
@ -164,10 +162,33 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f)
}
#ifndef Q_CLANG_QDOC
//
// Unsigned overflow math
//
namespace {
// Overflow math.
// This provides efficient implementations for int, unsigned, qsizetype and
// size_t. Implementations for 8- and 16-bit types will work but may not be as
// efficient. Implementations for 64-bit may be missing on 32-bit platforms.
#if (defined(Q_CC_GNU) && (Q_CC_GNU >= 500) || defined(Q_CC_INTEL)) || QT_HAS_BUILTIN(__builtin_add_overflowx)
// GCC 5, ICC 18, and Clang 3.8 have builtins to detect overflows
template <typename T> inline
typename std::enable_if<std::is_unsigned<T>::value || std::is_signed<T>::value, bool>::type
add_overflow(T v1, T v2, T *r)
{ return __builtin_add_overflow(v1, v2, r); }
template <typename T> inline
typename std::enable_if<std::is_unsigned<T>::value || std::is_signed<T>::value, bool>::type
sub_overflow(T v1, T v2, T *r)
{ return __builtin_sub_overflow(v1, v2, r); }
template <typename T> inline
typename std::enable_if<std::is_unsigned<T>::value || std::is_signed<T>::value, bool>::type
mul_overflow(T v1, T v2, T *r)
{ return __builtin_mul_overflow(v1, v2, r); }
#else
// Generic implementations
template <typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
add_overflow(T v1, T v2, T *r)
{
@ -176,69 +197,92 @@ add_overflow(T v1, T v2, T *r)
return v1 > T(v1 + v2);
}
template <typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type
add_overflow(T v1, T v2, T *r)
{
// Here's how we calculate the overflow:
// 1) unsigned addition is well-defined, so we can always execute it
// 2) conversion from unsigned back to signed is implementation-
// defined and in the implementations we use, it's a no-op.
// 3) signed integer overflow happens if the sign of the two input operands
// is the same but the sign of the result is different. In other words,
// the sign of the result must be the same as the sign of either
// operand.
using U = typename std::make_unsigned<T>::type;
*r = T(U(v1) + U(v2));
// If int is two's complement, assume all integer types are too.
if (std::is_same<int32_t, int>::value) {
// Two's complement equivalent (generates slightly shorter code):
// x ^ y is negative if x and y have different signs
// x & y is negative if x and y are negative
// (x ^ z) & (y ^ z) is negative if x and z have different signs
// AND y and z have different signs
return ((v1 ^ *r) & (v2 ^ *r)) < 0;
}
bool s1 = (v1 < 0);
bool s2 = (v2 < 0);
bool sr = (*r < 0);
return s1 != sr && s2 != sr;
// also: return s1 == s2 && s1 != sr;
}
template <typename T> inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
sub_overflow(T v1, T v2, T *r)
{
// unsigned subtractions are well-defined
*r = v1 - v2;
return v1 < v2;
}
template <typename T> inline typename std::enable_if<std::is_signed<T>::value, bool>::type
sub_overflow(T v1, T v2, T *r)
{
// See above for explanation. This is the same with some signs reversed.
// We can't use add_overflow(v1, -v2, r) because it would be UB if
// v2 == std::numeric_limits<T>::min().
using U = typename std::make_unsigned<T>::type;
*r = T(U(v1) - U(v2));
if (std::is_same<int32_t, int>::value)
return ((v1 ^ *r) & (~v2 ^ *r)) < 0;
bool s1 = (v1 < 0);
bool s2 = !(v2 < 0);
bool sr = (*r < 0);
return s1 != sr && s2 != sr;
// also: return s1 == s2 && s1 != sr;
}
template <typename T> inline
typename std::enable_if<std::is_unsigned<T>::value || std::is_signed<T>::value, bool>::type
mul_overflow(T v1, T v2, T *r)
{
// use the next biggest type
// Note: for 64-bit systems where __int128 isn't supported, this will cause an error.
// A fallback is present below.
typedef typename QIntegerForSize<sizeof(T) * 2>::Unsigned Larger;
using LargerInt = QIntegerForSize<sizeof(T) * 2>;
using Larger = typename std::conditional<std::is_signed<T>::value,
typename LargerInt::Signed, typename LargerInt::Unsigned>::type;
Larger lr = Larger(v1) * Larger(v2);
*r = T(lr);
return lr > std::numeric_limits<T>::max();
return lr > std::numeric_limits<T>::max() || lr < std::numeric_limits<T>::min();
}
#if defined(__SIZEOF_INT128__)
# define HAVE_MUL64_OVERFLOW
#endif
// GCC 5 and Clang have builtins to detect overflows
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uadd_overflow)
template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r)
{ return __builtin_uadd_overflow(v1, v2, r); }
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddl_overflow)
template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
{ return __builtin_uaddl_overflow(v1, v2, r); }
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddll_overflow)
template <> inline bool add_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r)
{ return __builtin_uaddll_overflow(v1, v2, r); }
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umul_overflow)
template <> inline bool mul_overflow(unsigned v1, unsigned v2, unsigned *r)
{ return __builtin_umul_overflow(v1, v2, r); }
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umull_overflow)
template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
{ return __builtin_umull_overflow(v1, v2, r); }
#endif
#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umulll_overflow)
template <> inline bool mul_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r)
{ return __builtin_umulll_overflow(v1, v2, r); }
# define HAVE_MUL64_OVERFLOW
#endif
#if (defined(Q_CC_MSVC) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86) && !QT_HAS_BUILTIN(__builtin_uadd_overflow)
# if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86)
// We can use intrinsics for the unsigned operations with MSVC
template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r)
{ return _addcarry_u32(0, v1, v2, r); }
# ifdef Q_CC_MSVC // longs are 32-bit
template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
{ return _addcarry_u32(0, v1, v2, reinterpret_cast<unsigned *>(r)); }
# endif
#endif
#if (defined(Q_CC_MSVC) || defined(Q_CC_INTEL)) && defined(Q_PROCESSOR_X86_64) && !QT_HAS_BUILTIN(__builtin_uadd_overflow)
// 32-bit mul_overflow is fine with the generic code above
# if defined(Q_PROCESSOR_X86_64)
template <> inline bool add_overflow(quint64 v1, quint64 v2, quint64 *r)
{ return _addcarry_u64(0, v1, v2, reinterpret_cast<unsigned __int64 *>(r)); }
# ifndef Q_CC_MSVC // longs are 64-bit
template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
{ return _addcarry_u64(0, v1, v2, reinterpret_cast<unsigned __int64 *>(r)); }
# endif
#endif
#if defined(Q_CC_MSVC) && (defined(Q_PROCESSOR_X86_64) || defined(Q_PROCESSOR_IA64)) && !QT_HAS_BUILTIN(__builtin_uadd_overflow)
#pragma intrinsic(_umul128)
# pragma intrinsic(_umul128)
template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r)
{
// use 128-bit multiplication with the _umul128 intrinsic
@ -247,117 +291,30 @@ template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r)
*r = _umul128(v1, v2, &high);
return high;
}
# define HAVE_MUL64_OVERFLOW
#endif
#if !defined(HAVE_MUL64_OVERFLOW) && defined(__LP64__)
// no 128-bit multiplication, we need to figure out with a slow division
template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r)
# pragma intrinsic(_mul128)
template <> inline bool mul_overflow(qint64 v1, qint64 v2, qint64 *r)
{
if (v2 && v1 > std::numeric_limits<quint64>::max() / v2)
return true;
*r = v1 * v2;
return false;
}
template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r)
{
return mul_overflow<quint64>(v1, v2, reinterpret_cast<quint64 *>(r));
}
#else
# undef HAVE_MUL64_OVERFLOW
#endif
// Use 128-bit multiplication with the _mul128 intrinsic
// https://msdn.microsoft.com/en-us/library/82cxdw50.aspx
//
// 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;
// This is slightly more complex than the unsigned case above: the sign bit
// of 'low' must be replicated as the entire 'high', so the only valid
// values for 'high' are 0 and -1.
asm ("addl %2, %1\n"
"seto %0"
: "=q" (overflow), "=r" (res)
: "r" (v2), "1" (res)
: "cc"
);
*r = res;
return overflow;
qint64 high;
*r = _mul128(v1, v2, &high);
if (high == 0)
return *r < 0;
if (high == -1)
return *r >= 0;
return true;
}
#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 // x86-64
# endif // MSVC x86
#endif // !GCC
}
#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
#endif // Q_CLANG_QDOC
}
QT_END_NAMESPACE

View File

@ -223,11 +223,20 @@ void tst_QNumeric::floatDistance_double()
void tst_QNumeric::addOverflow_data()
{
QTest::addColumn<int>("size");
// for unsigned, all sizes are supported
QTest::newRow("quint8") << 8;
QTest::newRow("quint16") << 16;
QTest::newRow("quint32") << 32;
QTest::newRow("quint64") << 64;
QTest::newRow("ulong") << 48; // it's either 32- or 64-bit, so on average it's 48 :-)
// for signed, we can't guarantee 64-bit
QTest::newRow("qint8") << -8;
QTest::newRow("qint16") << -16;
QTest::newRow("qint32") << -32;
if (sizeof(void *) == sizeof(qint64))
QTest::newRow("qint64") << -64;
}
// Note: in release mode, all the tests may be statically determined and only the calls
@ -238,6 +247,7 @@ template <typename Int> static void addOverflow_template()
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling in release mode");
#else
const Int max = std::numeric_limits<Int>::max();
const Int min = std::numeric_limits<Int>::min();
Int r;
// basic values
@ -248,15 +258,50 @@ template <typename Int> static void addOverflow_template()
QCOMPARE(add_overflow(Int(0), Int(1), &r), false);
QCOMPARE(r, Int(1));
QCOMPARE(sub_overflow(Int(0), Int(0), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(sub_overflow(Int(1), Int(0), &r), false);
QCOMPARE(r, Int(1));
QCOMPARE(sub_overflow(Int(1), Int(1), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(sub_overflow(Int(0), Int(1), &r), !min);
if (min)
QCOMPARE(r, Int(-1));
// half-way through max
QCOMPARE(add_overflow(Int(max/2), Int(max/2), &r), false);
QCOMPARE(r, Int(max / 2 * 2));
QCOMPARE(sub_overflow(Int(max/2), Int(max/2), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(add_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), false);
QCOMPARE(r, Int(max / 2 * 2));
QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), !min);
if (min)
QCOMPARE(r, Int(-2));
QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2), &r), false);
QCOMPARE(r, max);
QCOMPARE(sub_overflow(Int(max/2 + 1), Int(max/2), &r), false);
QCOMPARE(r, Int(1));
QCOMPARE(add_overflow(Int(max/2), Int(max/2 + 1), &r), false);
QCOMPARE(r, max);
QCOMPARE(sub_overflow(Int(max/2), Int(max/2 + 1), &r), !min);
if (min)
QCOMPARE(r, Int(-1));
QCOMPARE(add_overflow(Int(min/2), Int(min/2), &r), false);
QCOMPARE(r, Int(min / 2 * 2));
QCOMPARE(sub_overflow(Int(min/2), Int(min/2), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(add_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), !min);
if (min)
QCOMPARE(r, Int(min / 2 * 2));
QCOMPARE(sub_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), false);
QCOMPARE(r, Int(-2));
QCOMPARE(sub_overflow(Int(min/2 + 1), Int(min/2), &r), false);
QCOMPARE(r, Int(1));
QCOMPARE(sub_overflow(Int(min/2), Int(min/2 + 1), &r), !min);
if (min)
QCOMPARE(r, Int(-1));
// more than half
QCOMPARE(add_overflow(Int(max/4 * 3), Int(max/4), &r), false);
@ -265,19 +310,55 @@ template <typename Int> static void addOverflow_template()
// max
QCOMPARE(add_overflow(max, Int(0), &r), false);
QCOMPARE(r, max);
QCOMPARE(sub_overflow(max, Int(0), &r), false);
QCOMPARE(r, max);
QCOMPARE(add_overflow(Int(0), max, &r), false);
QCOMPARE(r, max);
QCOMPARE(sub_overflow(Int(0), max, &r), !min);
if (min)
QCOMPARE(r, Int(-max));
QCOMPARE(add_overflow(min, Int(0), &r), false);
QCOMPARE(r, min);
QCOMPARE(sub_overflow(min, Int(0), &r), false);
QCOMPARE(r, min);
QCOMPARE(add_overflow(Int(0), min, &r), false);
QCOMPARE(r, min);
QCOMPARE(sub_overflow(Int(0), Int(min+1), &r), !min);
if (min)
QCOMPARE(r, Int(-(min+1)));
// 64-bit issues
if (max > std::numeric_limits<uint>::max()) {
QCOMPARE(add_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false);
QCOMPARE(r, Int(2 * Int(std::numeric_limits<uint>::max())));
QCOMPARE(sub_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false);
QCOMPARE(r, Int(0));
}
if (min && min < -Int(std::numeric_limits<uint>::max())) {
QCOMPARE(add_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false);
QCOMPARE(r, Int(-2 * Int(std::numeric_limits<uint>::max())));
QCOMPARE(sub_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false);
QCOMPARE(r, Int(0));
}
// overflows
// overflows past max
QCOMPARE(add_overflow(max, Int(1), &r), true);
QCOMPARE(add_overflow(Int(1), max, &r), true);
QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2 + 1), &r), true);
if (!min) {
QCOMPARE(sub_overflow(Int(-max), Int(-2), &r), true);
QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), true);
}
// overflows past min (in case of min == 0, repeats some tests above)
if (min) {
QCOMPARE(sub_overflow(min, Int(1), &r), true);
QCOMPARE(sub_overflow(Int(1), min, &r), true);
QCOMPARE(sub_overflow(Int(min/2 - 1), Int(-Int(min/2)), &r), true);
QCOMPARE(add_overflow(min, Int(-1), &r), true);
QCOMPARE(add_overflow(Int(-1), min, &r), true);
}
#endif
}
@ -294,6 +375,15 @@ void tst_QNumeric::addOverflow()
addOverflow_template<ulong>(); // not really 48-bit
if (size == 64)
addOverflow_template<quint64>();
if (size == -8)
addOverflow_template<qint8>();
if (size == -16)
addOverflow_template<qint16>();
if (size == -32)
addOverflow_template<qint32>();
if (size == -64)
addOverflow_template<qint64>();
}
void tst_QNumeric::mulOverflow_data()
@ -309,7 +399,13 @@ template <typename Int> static void mulOverflow_template()
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling");
#else
const Int max = std::numeric_limits<Int>::max();
const Int middle = Int(max >> (sizeof(Int) * CHAR_BIT / 2));
const Int min = std::numeric_limits<Int>::min();
// for unsigned (even number of significant bits): mid2 = mid1 - 1
// for signed (odd number of significant bits): mid2 = mid1 / 2 - 1
const Int mid1 = Int(Int(1) << sizeof(Int) * CHAR_BIT / 2);
const Int mid2 = (std::numeric_limits<Int>::digits % 2 ? mid1 / 2 : mid1) - 1;
Int r;
// basic multiplications
@ -323,6 +419,10 @@ template <typename Int> static void mulOverflow_template()
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(Int(0), max, &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(min, Int(0), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(Int(0), min, &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(Int(1), Int(1), &r), false);
QCOMPARE(r, Int(1));
@ -330,23 +430,45 @@ template <typename Int> static void mulOverflow_template()
QCOMPARE(r, max);
QCOMPARE(mul_overflow(max, Int(1), &r), false);
QCOMPARE(r, max);
QCOMPARE(mul_overflow(Int(1), min, &r), false);
QCOMPARE(r, min);
QCOMPARE(mul_overflow(min, Int(1), &r), false);
QCOMPARE(r, min);
// almost max
QCOMPARE(mul_overflow(middle, middle, &r), false);
QCOMPARE(r, Int(max - 2 * middle));
QCOMPARE(mul_overflow(Int(middle + 1), middle, &r), false);
QCOMPARE(r, Int(middle << (sizeof(Int) * CHAR_BIT / 2)));
QCOMPARE(mul_overflow(middle, Int(middle + 1), &r), false);
QCOMPARE(r, Int(middle << (sizeof(Int) * CHAR_BIT / 2)));
QCOMPARE(mul_overflow(mid1, mid2, &r), false);
QCOMPARE(r, Int(max - mid1 + 1));
QCOMPARE(mul_overflow(Int(max / 2), Int(2), &r), false);
QCOMPARE(r, Int(max & ~Int(1)));
QCOMPARE(mul_overflow(Int(max / 4), Int(4), &r), false);
QCOMPARE(r, Int(max & ~Int(3)));
if (min) {
QCOMPARE(mul_overflow(Int(-mid1), mid2, &r), false);
QCOMPARE(r, Int(-max + mid1 - 1));
QCOMPARE(mul_overflow(Int(-max / 2), Int(2), &r), false);
QCOMPARE(r, Int(-max + 1));
QCOMPARE(mul_overflow(Int(-max / 4), Int(4), &r), false);
QCOMPARE(r, Int(-max + 3));
QCOMPARE(mul_overflow(Int(-mid1), Int(mid2 + 1), &r), false);
QCOMPARE(r, min);
QCOMPARE(mul_overflow(mid1, Int(-mid2 - 1), &r), false);
QCOMPARE(r, min);
}
// overflows
QCOMPARE(mul_overflow(max, Int(2), &r), true);
QCOMPARE(mul_overflow(Int(max / 2), Int(3), &r), true);
QCOMPARE(mul_overflow(Int(middle + 1), Int(middle + 1), &r), true);
QCOMPARE(mul_overflow(mid1, Int(mid2 + 1), &r), true);
QCOMPARE(mul_overflow(Int(max / 2 + 2), Int(2), &r), true);
QCOMPARE(mul_overflow(Int(1ULL << (std::numeric_limits<Int>::digits - 1)), Int(2), &r), true);
if (min) {
QCOMPARE(mul_overflow(min, Int(2), &r), true);
QCOMPARE(mul_overflow(Int(min / 2), Int(3), &r), true);
QCOMPARE(mul_overflow(Int(min / 2 - 1), Int(2), &r), true);
QCOMPARE(mul_overflow(Int(min + min/2), Int(2), &r), true);
}
#endif
}
@ -373,6 +495,20 @@ void tst_QNumeric::mulOverflow()
MulOverflowDispatch<ulong>()(); // not really 48-bit
if (size == 64)
MulOverflowDispatch<quint64>()();
if (size == -8)
MulOverflowDispatch<qint8>()();
if (size == -16)
MulOverflowDispatch<qint16>()();
if (size == -32)
MulOverflowDispatch<qint32>()();
if (size == -64) {
#if QT_POINTER_SIZE == 8
MulOverflowDispatch<qint64>()();
#else
QFAIL("128-bit multiplication not supported on this platform");
#endif
}
}
void tst_QNumeric::signedOverflow()