blob: e1b83369550687d03a2af9cba06bb9a235b0dbd3 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtGlobal>
#include "private/qnumeric_p.h"
#include <math.h>
#include <float.h>
namespace {
template <typename F> struct Fuzzy {};
/* Data taken from qglobal.h's implementation of qFuzzyCompare:
* qFuzzyCompare conflates values with fractional difference up to (and
* including) the given scale.
*/
template <> struct Fuzzy<double> { constexpr static double scale = 1e12; };
template <> struct Fuzzy<float> { constexpr static float scale = 1e5f; };
}
class tst_QNumeric: public QObject
{
Q_OBJECT
// Support for floating-point:
template<typename F> inline void fuzzyCompare_data();
template<typename F> inline void fuzzyCompare();
template<typename F> inline void checkNaN(F nan);
template<typename F> inline void rawNaN_data();
template<typename F> inline void rawNaN();
#if QT_CONFIG(signaling_nan)
template<typename F> inline void distinctNaN();
#endif
template<typename F, typename Whole> inline void generalNaN_data();
template<typename F, typename Whole> inline void generalNaN();
template<typename F> inline void infinity();
template<typename F> inline void classifyfp();
template<typename F, typename Count> inline void distance_data();
template<typename F, typename Count> inline void distance();
private slots:
// Floating-point tests:
void fuzzyCompareF_data() { fuzzyCompare_data<float>(); }
void fuzzyCompareF() { fuzzyCompare<float>(); }
void fuzzyCompareD_data() { fuzzyCompare_data<double>(); }
void fuzzyCompareD() { fuzzyCompare<double>(); }
void rawNaNF_data() { rawNaN_data<float>(); }
void rawNaNF() { rawNaN<float>(); }
void rawNaND_data() { rawNaN_data<double>(); }
void rawNaND() { rawNaN<double>(); }
#if QT_CONFIG(signaling_nan)
void distinctNaNF();
void distinctNaND() { distinctNaN<double>(); }
#endif
void generalNaNd_data() { generalNaN_data<double, quint64>(); }
void generalNaNd() { generalNaN<double, quint64>(); }
void generalNaNf_data() { generalNaN_data<float, quint32>(); }
void generalNaNf() { generalNaN<float, quint32>(); }
void infinityF() { infinity<float>(); }
void infinityD() { infinity<double>(); }
void classifyF() { classifyfp<float>(); }
void classifyD() { classifyfp<double>(); }
void floatDistance_data() { distance_data<float, quint32>(); }
void floatDistance() { distance<float, quint32>(); }
void doubleDistance_data() { distance_data<double, quint64>(); }
void doubleDistance() { distance<double, quint64>(); }
// Whole number tests:
void addOverflow_data();
void addOverflow();
void mulOverflow_data();
void mulOverflow();
void signedOverflow();
};
// Floating-point tests:
template<typename F>
void tst_QNumeric::fuzzyCompare_data()
{
QTest::addColumn<F>("val1");
QTest::addColumn<F>("val2");
QTest::addColumn<bool>("isEqual");
const F zero(0), one(1), ten(10);
const F huge = Fuzzy<F>::scale, tiny = one / huge;
const F deci(.1), giga(1e9), nano(1e-9), big(1e7), small(1e-10);
QTest::newRow("zero") << zero << zero << true;
QTest::newRow("ten") << ten << ten << true;
QTest::newRow("large") << giga << giga << true;
QTest::newRow("small") << small << small << true;
QTest::newRow("10+9*tiny==10") << (ten + 9 * tiny) << ten << true;
QTest::newRow("huge+.9==huge") << (huge + 9 * deci) << huge << true;
QTest::newRow("eps2") << (ten + tiny) << (ten + 2 * tiny) << true;
QTest::newRow("eps9") << (ten + tiny) << (ten + 9 * tiny) << true;
QTest::newRow("0!=1") << zero << one << false;
QTest::newRow("0!=big") << zero << big << false;
QTest::newRow("0!=nano") << zero << nano << false;
QTest::newRow("giga!=nano") << giga << nano << false;
QTest::newRow("small!=nano") << small << nano << false;
QTest::newRow("huge+1.1!=huge") << (huge + 1 + deci) << huge << false;
QTest::newRow("1+1.1*tiny!=1") << (one + tiny * (one + deci)) << one << false;
}
template<typename F>
void tst_QNumeric::fuzzyCompare()
{
QFETCH(F, val1);
QFETCH(F, val2);
QFETCH(bool, isEqual);
QCOMPARE(::qFuzzyCompare(val1, val2), isEqual);
QCOMPARE(::qFuzzyCompare(val2, val1), isEqual);
QCOMPARE(::qFuzzyCompare(-val1, -val2), isEqual);
QCOMPARE(::qFuzzyCompare(-val2, -val1), isEqual);
}
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
// turn -ffast-math off
# pragma GCC optimize "no-fast-math"
#endif
template<typename F>
void tst_QNumeric::checkNaN(F nan)
{
#define CHECKNAN(value) \
do { \
const F v = (value); \
QCOMPARE(qFpClassify(v), FP_NAN); \
QVERIFY(qIsNaN(v)); \
QVERIFY(!qIsFinite(v)); \
QVERIFY(!qIsInf(v)); \
} while (0)
QVERIFY(!(0 > nan));
QVERIFY(!(0 < nan));
QVERIFY(!(0 == nan));
QVERIFY(!(nan == nan));
CHECKNAN(nan);
CHECKNAN(nan + 1);
CHECKNAN(nan - 1);
CHECKNAN(-nan);
CHECKNAN(nan * 2.0);
CHECKNAN(nan / 2.0);
CHECKNAN(1.0 / nan);
CHECKNAN(0.0 / nan);
CHECKNAN(0.0 * nan);
// When any NaN is expected, any NaN will do:
QCOMPARE(nan, nan);
QCOMPARE(nan, -nan);
QCOMPARE(nan, qQNaN());
#undef CHECKNAN
}
template<typename F>
void tst_QNumeric::rawNaN_data()
{
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404)
QSKIP("Non-conformant fast math mode is enabled, cannot run test");
#endif
QTest::addColumn<F>("nan");
QTest::newRow("quiet") << F(qQNaN());
#if QT_CONFIG(signaling_nan)
QTest::newRow("signaling") << F(qSNaN());
#endif
}
template<typename F>
void tst_QNumeric::rawNaN()
{
QFETCH(F, nan);
#ifdef Q_OS_WASM
# ifdef __asmjs
QEXPECT_FAIL("", "Fastcomp conflates quiet and signaling NaNs", Continue);
# endif // but the modern clang compiler handls it fine.
#endif
checkNaN(nan);
}
#if QT_CONFIG(signaling_nan)
template<typename F>
void tst_QNumeric::distinctNaN()
{
const F qnan = qQNaN();
const F snan = qSNaN();
QVERIFY(memcmp(&qnan, &snan, sizeof(F)) != 0);
}
void tst_QNumeric::distinctNaNF() {
#ifdef Q_CC_MSVC
QEXPECT_FAIL("", "MSVC's float conflates quiet and signaling NaNs", Continue);
#endif
distinctNaN<float>();
}
#endif // signaling_nan
template<typename F, typename Whole>
void tst_QNumeric::generalNaN_data()
{
Q_STATIC_ASSERT(sizeof(F) == sizeof(Whole));
QTest::addColumn<Whole>("whole");
// Every value with every bit of the exponent set is a NaN.
// Sign and mantissa can be anything without interfering with that.
using Bounds = std::numeric_limits<F>;
// Bounds::digits is one more than the number of bits used to encode the mantissa:
const int mantissaBits = Bounds::digits - 1;
// One bit for sign, the rest are mantissa and exponent:
const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits;
const Whole exponent = ((Whole(1) << exponentBits) - 1) << mantissaBits;
const Whole sign = Whole(1) << (exponentBits + mantissaBits);
const Whole mantissaTop = Whole(1) << (mantissaBits - 1);
QTest::newRow("lowload") << (exponent | 1);
QTest::newRow("sign-lowload") << (sign | exponent | 1);
QTest::newRow("highload") << (exponent | mantissaTop);
QTest::newRow("sign-highload") << (sign | exponent | mantissaTop);
}
template<typename F, typename Whole>
void tst_QNumeric::generalNaN()
{
Q_STATIC_ASSERT(sizeof(F) == sizeof(Whole));
QFETCH(const Whole, whole);
F nan;
memcpy(&nan, &whole, sizeof(F));
checkNaN(nan);
}
template<typename F>
void tst_QNumeric::infinity()
{
const F inf = qInf();
const F zero(0), one(1), two(2);
QVERIFY(inf > zero);
QVERIFY(-inf < zero);
QVERIFY(qIsInf(inf));
QCOMPARE(inf, inf);
QCOMPARE(-inf, -inf);
QVERIFY(qIsInf(-inf));
QVERIFY(qIsInf(inf + one));
QVERIFY(qIsInf(inf - one));
QVERIFY(qIsInf(-inf - one));
QVERIFY(qIsInf(-inf + one));
QVERIFY(qIsInf(inf * two));
QVERIFY(qIsInf(-inf * two));
QVERIFY(qIsInf(inf / two));
QVERIFY(qIsInf(-inf / two));
QVERIFY(qFuzzyCompare(one / inf, zero));
QCOMPARE(1.0 / inf, 0.0);
QVERIFY(qFuzzyCompare(one / -inf, zero));
QCOMPARE(one / -inf, zero);
QVERIFY(qIsNaN(zero * inf));
QVERIFY(qIsNaN(zero * -inf));
}
template<typename F>
void tst_QNumeric::classifyfp()
{
using Bounds = std::numeric_limits<F>;
const F huge = Bounds::max();
const F tiny = Bounds::min();
// NaNs already handled, see checkNaN()'s callers.
const F one(1), two(2), inf(qInf());
QCOMPARE(qFpClassify(inf), FP_INFINITE);
QCOMPARE(qFpClassify(-inf), FP_INFINITE);
QCOMPARE(qFpClassify(huge * two), FP_INFINITE);
QCOMPARE(qFpClassify(huge * -two), FP_INFINITE);
QCOMPARE(qFpClassify(one), FP_NORMAL);
QCOMPARE(qFpClassify(huge), FP_NORMAL);
QCOMPARE(qFpClassify(-huge), FP_NORMAL);
QCOMPARE(qFpClassify(tiny), FP_NORMAL);
QCOMPARE(qFpClassify(-tiny), FP_NORMAL);
if (Bounds::has_denorm == std::denorm_present) {
QCOMPARE(qFpClassify(tiny / two), FP_SUBNORMAL);
QCOMPARE(qFpClassify(tiny / -two), FP_SUBNORMAL);
}
}
template<typename F, typename Count>
void tst_QNumeric::distance_data()
{
using Bounds = std::numeric_limits<F>;
const F huge = Bounds::max();
const F tiny = Bounds::min();
QTest::addColumn<F>("from");
QTest::addColumn<F>("stop");
QTest::addColumn<Count>("expectedDistance");
using Bounds = std::numeric_limits<F>;
const int mantissaBits = Bounds::digits - 1;
const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits;
// Set to 1 and 0 if denormals are not included:
const Count count_0_to_tiny = Count(1) << mantissaBits;
const Count count_denormals = count_0_to_tiny - 1;
// We need +1 to include the 0:
const Count count_0_to_1
= (Count(1) << mantissaBits) * ((Count(1) << (exponentBits - 1)) - 2)
+ 1 + count_denormals;
const Count count_1_to_2 = Count(1) << mantissaBits;
// We don't need +1 because huge has all bits set in the mantissa. (Thus mantissa
// have not wrapped back to 0, which would be the case for 1 in _0_to_1
const Count count_0_to_huge
= (Count(1) << mantissaBits) * ((Count(1) << exponentBits) - 2)
+ count_denormals;
const F zero(0), half(.5), one(1), sesqui(1.5), two(2);
const F denormal = tiny / two;
QTest::newRow("[0,tiny]") << zero << tiny << count_0_to_tiny;
QTest::newRow("[0,huge]") << zero << huge << count_0_to_huge;
QTest::newRow("[1,1.5]") << one << sesqui << (Count(1) << (mantissaBits - 1));
QTest::newRow("[0,1]") << zero << one << count_0_to_1;
QTest::newRow("[0.5,1]") << half << one << (Count(1) << mantissaBits);
QTest::newRow("[1,2]") << one << two << count_1_to_2;
QTest::newRow("[-1,+1]") << -one << +one << 2 * count_0_to_1;
QTest::newRow("[-1,0]") << -one << zero << count_0_to_1;
QTest::newRow("[-1,huge]") << -one << huge << count_0_to_1 + count_0_to_huge;
QTest::newRow("[-2,-1") << -two << -one << count_1_to_2;
QTest::newRow("[-1,-2") << -one << -two << count_1_to_2;
QTest::newRow("[tiny,huge]") << tiny << huge << count_0_to_huge - count_0_to_tiny;
QTest::newRow("[-huge,huge]") << -huge << huge << (2 * count_0_to_huge);
QTest::newRow("denormal") << zero << denormal << count_0_to_tiny / 2;
}
template<typename F, typename Count>
void tst_QNumeric::distance()
{
QFETCH(F, from);
QFETCH(F, stop);
QFETCH(Count, expectedDistance);
#ifdef Q_OS_QNX
QEXPECT_FAIL("denormal", "See QTBUG-37094", Continue);
#endif
QCOMPARE(qFloatDistance(from, stop), expectedDistance);
}
// Whole number tests:
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
// to QTest::toString and QTest::qCompare will remain.
template <typename Int> static void addOverflow_template()
{
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 2000
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
QCOMPARE(add_overflow(Int(0), Int(0), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(add_overflow(Int(1), Int(0), &r), false);
QCOMPARE(r, Int(1));
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);
QCOMPARE(r, Int(max / 4 * 4));
// 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 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
}
void tst_QNumeric::addOverflow()
{
QFETCH(int, size);
if (size == 8)
addOverflow_template<quint8>();
if (size == 16)
addOverflow_template<quint16>();
if (size == 32)
addOverflow_template<quint32>();
if (size == 48)
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()
{
addOverflow_data();
}
// Note: in release mode, all the tests may be statically determined and only the calls
// to QTest::toString and QTest::qCompare will remain.
template <typename Int> static void mulOverflow_template()
{
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 1900
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling");
#else
const Int max = std::numeric_limits<Int>::max();
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
QCOMPARE(mul_overflow(Int(0), Int(0), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(Int(1), Int(0), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(Int(0), Int(1), &r), false);
QCOMPARE(r, Int(0));
QCOMPARE(mul_overflow(max, Int(0), &r), false);
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));
QCOMPARE(mul_overflow(Int(1), max, &r), false);
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(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(mid1, Int(mid2 + 1), &r), true);
QCOMPARE(mul_overflow(Int(max / 2 + 2), Int(2), &r), true);
QCOMPARE(mul_overflow(Int(max - max / 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);
}
#endif
}
template <typename Int, bool enabled = sizeof(Int) <= sizeof(void*)> struct MulOverflowDispatch;
template <typename Int> struct MulOverflowDispatch<Int, true>
{
void operator()() { mulOverflow_template<Int>(); }
};
template <typename Int> struct MulOverflowDispatch<Int, false>
{
void operator()() { QSKIP("This type is too big for this architecture"); }
};
void tst_QNumeric::mulOverflow()
{
QFETCH(int, size);
if (size == 8)
MulOverflowDispatch<quint8>()();
if (size == 16)
MulOverflowDispatch<quint16>()();
if (size == 32)
MulOverflowDispatch<quint32>()();
if (size == 48)
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()
{
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"