| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2019 The Qt Company Ltd. |
| 4 | ** Copyright (C) 2016 Intel Corporation. |
| 5 | ** Contact: https://www.qt.io/licensing/ |
| 6 | ** |
| 7 | ** This file is part of the test suite of the Qt Toolkit. |
| 8 | ** |
| 9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 10 | ** Commercial License Usage |
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 12 | ** accordance with the commercial license agreement provided with the |
| 13 | ** Software or, alternatively, in accordance with the terms contained in |
| 14 | ** a written agreement between you and The Qt Company. For licensing terms |
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 16 | ** information use the contact form at https://www.qt.io/contact-us. |
| 17 | ** |
| 18 | ** GNU General Public License Usage |
| 19 | ** Alternatively, this file may be used under the terms of the GNU |
| 20 | ** General Public License version 3 as published by the Free Software |
| 21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 22 | ** included in the packaging of this file. Please review the following |
| 23 | ** information to ensure the GNU General Public License requirements will |
| 24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 25 | ** |
| 26 | ** $QT_END_LICENSE$ |
| 27 | ** |
| 28 | ****************************************************************************/ |
| 29 | |
| 30 | |
| 31 | #include <QtTest/QtTest> |
| 32 | #include <QtGlobal> |
| 33 | #include "private/qnumeric_p.h" |
| 34 | |
| 35 | #include <math.h> |
| 36 | #include <float.h> |
| 37 | |
| 38 | namespace { |
| 39 | template <typename F> struct Fuzzy {}; |
| 40 | /* Data taken from qglobal.h's implementation of qFuzzyCompare: |
| 41 | * qFuzzyCompare conflates values with fractional difference up to (and |
| 42 | * including) the given scale. |
| 43 | */ |
| 44 | template <> struct Fuzzy<double> { constexpr static double scale = 1e12; }; |
| 45 | template <> struct Fuzzy<float> { constexpr static float scale = 1e5f; }; |
| 46 | } |
| 47 | |
| 48 | class tst_QNumeric: public QObject |
| 49 | { |
| 50 | Q_OBJECT |
| 51 | |
| 52 | // Support for floating-point: |
| 53 | template<typename F> inline void fuzzyCompare_data(); |
| 54 | template<typename F> inline void fuzzyCompare(); |
| 55 | template<typename F> inline void checkNaN(F nan); |
| 56 | template<typename F> inline void rawNaN_data(); |
| 57 | template<typename F> inline void rawNaN(); |
| 58 | #if QT_CONFIG(signaling_nan) |
| 59 | template<typename F> inline void distinctNaN(); |
| 60 | #endif |
| 61 | template<typename F, typename Whole> inline void generalNaN_data(); |
| 62 | template<typename F, typename Whole> inline void generalNaN(); |
| 63 | template<typename F> inline void infinity(); |
| 64 | template<typename F> inline void classifyfp(); |
| 65 | template<typename F, typename Count> inline void distance_data(); |
| 66 | template<typename F, typename Count> inline void distance(); |
| 67 | |
| 68 | private slots: |
| 69 | // Floating-point tests: |
| 70 | void fuzzyCompareF_data() { fuzzyCompare_data<float>(); } |
| 71 | void fuzzyCompareF() { fuzzyCompare<float>(); } |
| 72 | void fuzzyCompareD_data() { fuzzyCompare_data<double>(); } |
| 73 | void fuzzyCompareD() { fuzzyCompare<double>(); } |
| 74 | void rawNaNF_data() { rawNaN_data<float>(); } |
| 75 | void rawNaNF() { rawNaN<float>(); } |
| 76 | void rawNaND_data() { rawNaN_data<double>(); } |
| 77 | void rawNaND() { rawNaN<double>(); } |
| 78 | #if QT_CONFIG(signaling_nan) |
| 79 | void distinctNaNF(); |
| 80 | void distinctNaND() { distinctNaN<double>(); } |
| 81 | #endif |
| 82 | void generalNaNd_data() { generalNaN_data<double, quint64>(); } |
| 83 | void generalNaNd() { generalNaN<double, quint64>(); } |
| 84 | void generalNaNf_data() { generalNaN_data<float, quint32>(); } |
| 85 | void generalNaNf() { generalNaN<float, quint32>(); } |
| 86 | void infinityF() { infinity<float>(); } |
| 87 | void infinityD() { infinity<double>(); } |
| 88 | void classifyF() { classifyfp<float>(); } |
| 89 | void classifyD() { classifyfp<double>(); } |
| 90 | void floatDistance_data() { distance_data<float, quint32>(); } |
| 91 | void floatDistance() { distance<float, quint32>(); } |
| 92 | void doubleDistance_data() { distance_data<double, quint64>(); } |
| 93 | void doubleDistance() { distance<double, quint64>(); } |
| 94 | |
| 95 | // Whole number tests: |
| 96 | void addOverflow_data(); |
| 97 | void addOverflow(); |
| 98 | void mulOverflow_data(); |
| 99 | void mulOverflow(); |
| 100 | void signedOverflow(); |
| 101 | }; |
| 102 | |
| 103 | // Floating-point tests: |
| 104 | |
| 105 | template<typename F> |
| 106 | void tst_QNumeric::fuzzyCompare_data() |
| 107 | { |
| 108 | QTest::addColumn<F>("val1" ); |
| 109 | QTest::addColumn<F>("val2" ); |
| 110 | QTest::addColumn<bool>(name: "isEqual" ); |
| 111 | const F zero(0), one(1), ten(10); |
| 112 | const F huge = Fuzzy<F>::scale, tiny = one / huge; |
| 113 | const F deci(.1), giga(1e9), nano(1e-9), big(1e7), small(1e-10); |
| 114 | |
| 115 | QTest::newRow(dataTag: "zero" ) << zero << zero << true; |
| 116 | QTest::newRow(dataTag: "ten" ) << ten << ten << true; |
| 117 | QTest::newRow(dataTag: "large" ) << giga << giga << true; |
| 118 | QTest::newRow(dataTag: "small" ) << small << small << true; |
| 119 | QTest::newRow(dataTag: "10+9*tiny==10" ) << (ten + 9 * tiny) << ten << true; |
| 120 | QTest::newRow(dataTag: "huge+.9==huge" ) << (huge + 9 * deci) << huge << true; |
| 121 | QTest::newRow(dataTag: "eps2" ) << (ten + tiny) << (ten + 2 * tiny) << true; |
| 122 | QTest::newRow(dataTag: "eps9" ) << (ten + tiny) << (ten + 9 * tiny) << true; |
| 123 | |
| 124 | QTest::newRow(dataTag: "0!=1" ) << zero << one << false; |
| 125 | QTest::newRow(dataTag: "0!=big" ) << zero << big << false; |
| 126 | QTest::newRow(dataTag: "0!=nano" ) << zero << nano << false; |
| 127 | QTest::newRow(dataTag: "giga!=nano" ) << giga << nano << false; |
| 128 | QTest::newRow(dataTag: "small!=nano" ) << small << nano << false; |
| 129 | QTest::newRow(dataTag: "huge+1.1!=huge" ) << (huge + 1 + deci) << huge << false; |
| 130 | QTest::newRow(dataTag: "1+1.1*tiny!=1" ) << (one + tiny * (one + deci)) << one << false; |
| 131 | } |
| 132 | |
| 133 | template<typename F> |
| 134 | void tst_QNumeric::fuzzyCompare() |
| 135 | { |
| 136 | QFETCH(F, val1); |
| 137 | QFETCH(F, val2); |
| 138 | QFETCH(bool, isEqual); |
| 139 | |
| 140 | QCOMPARE(::qFuzzyCompare(val1, val2), isEqual); |
| 141 | QCOMPARE(::qFuzzyCompare(val2, val1), isEqual); |
| 142 | QCOMPARE(::qFuzzyCompare(-val1, -val2), isEqual); |
| 143 | QCOMPARE(::qFuzzyCompare(-val2, -val1), isEqual); |
| 144 | } |
| 145 | |
| 146 | #if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) |
| 147 | // turn -ffast-math off |
| 148 | # pragma GCC optimize "no-fast-math" |
| 149 | #endif |
| 150 | |
| 151 | template<typename F> |
| 152 | void tst_QNumeric::checkNaN(F nan) |
| 153 | { |
| 154 | #define CHECKNAN(value) \ |
| 155 | do { \ |
| 156 | const F v = (value); \ |
| 157 | QCOMPARE(qFpClassify(v), FP_NAN); \ |
| 158 | QVERIFY(qIsNaN(v)); \ |
| 159 | QVERIFY(!qIsFinite(v)); \ |
| 160 | QVERIFY(!qIsInf(v)); \ |
| 161 | } while (0) |
| 162 | |
| 163 | QVERIFY(!(0 > nan)); |
| 164 | QVERIFY(!(0 < nan)); |
| 165 | QVERIFY(!(0 == nan)); |
| 166 | QVERIFY(!(nan == nan)); |
| 167 | |
| 168 | CHECKNAN(nan); |
| 169 | CHECKNAN(nan + 1); |
| 170 | CHECKNAN(nan - 1); |
| 171 | CHECKNAN(-nan); |
| 172 | CHECKNAN(nan * 2.0); |
| 173 | CHECKNAN(nan / 2.0); |
| 174 | CHECKNAN(1.0 / nan); |
| 175 | CHECKNAN(0.0 / nan); |
| 176 | CHECKNAN(0.0 * nan); |
| 177 | |
| 178 | // When any NaN is expected, any NaN will do: |
| 179 | QCOMPARE(nan, nan); |
| 180 | QCOMPARE(nan, -nan); |
| 181 | QCOMPARE(nan, qQNaN()); |
| 182 | #undef CHECKNAN |
| 183 | } |
| 184 | |
| 185 | template<typename F> |
| 186 | void tst_QNumeric::rawNaN_data() |
| 187 | { |
| 188 | #if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404) |
| 189 | QSKIP("Non-conformant fast math mode is enabled, cannot run test" ); |
| 190 | #endif |
| 191 | QTest::addColumn<F>("nan" ); |
| 192 | |
| 193 | QTest::newRow(dataTag: "quiet" ) << F(qQNaN()); |
| 194 | #if QT_CONFIG(signaling_nan) |
| 195 | QTest::newRow(dataTag: "signaling" ) << F(qSNaN()); |
| 196 | #endif |
| 197 | } |
| 198 | |
| 199 | template<typename F> |
| 200 | void tst_QNumeric::rawNaN() |
| 201 | { |
| 202 | QFETCH(F, nan); |
| 203 | #ifdef Q_OS_WASM |
| 204 | # ifdef __asmjs |
| 205 | QEXPECT_FAIL("" , "Fastcomp conflates quiet and signaling NaNs" , Continue); |
| 206 | # endif // but the modern clang compiler handls it fine. |
| 207 | #endif |
| 208 | checkNaN(nan); |
| 209 | } |
| 210 | |
| 211 | #if QT_CONFIG(signaling_nan) |
| 212 | template<typename F> |
| 213 | void tst_QNumeric::distinctNaN() |
| 214 | { |
| 215 | const F qnan = qQNaN(); |
| 216 | const F snan = qSNaN(); |
| 217 | QVERIFY(memcmp(&qnan, &snan, sizeof(F)) != 0); |
| 218 | } |
| 219 | |
| 220 | void tst_QNumeric::distinctNaNF() { |
| 221 | #ifdef Q_CC_MSVC |
| 222 | QEXPECT_FAIL("" , "MSVC's float conflates quiet and signaling NaNs" , Continue); |
| 223 | #endif |
| 224 | distinctNaN<float>(); |
| 225 | } |
| 226 | #endif // signaling_nan |
| 227 | |
| 228 | template<typename F, typename Whole> |
| 229 | void tst_QNumeric::generalNaN_data() |
| 230 | { |
| 231 | Q_STATIC_ASSERT(sizeof(F) == sizeof(Whole)); |
| 232 | QTest::addColumn<Whole>("whole" ); |
| 233 | // Every value with every bit of the exponent set is a NaN. |
| 234 | // Sign and mantissa can be anything without interfering with that. |
| 235 | using Bounds = std::numeric_limits<F>; |
| 236 | // Bounds::digits is one more than the number of bits used to encode the mantissa: |
| 237 | const int mantissaBits = Bounds::digits - 1; |
| 238 | // One bit for sign, the rest are mantissa and exponent: |
| 239 | const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits; |
| 240 | |
| 241 | const Whole exponent = ((Whole(1) << exponentBits) - 1) << mantissaBits; |
| 242 | const Whole sign = Whole(1) << (exponentBits + mantissaBits); |
| 243 | const Whole mantissaTop = Whole(1) << (mantissaBits - 1); |
| 244 | |
| 245 | QTest::newRow(dataTag: "lowload" ) << (exponent | 1); |
| 246 | QTest::newRow(dataTag: "sign-lowload" ) << (sign | exponent | 1); |
| 247 | QTest::newRow(dataTag: "highload" ) << (exponent | mantissaTop); |
| 248 | QTest::newRow(dataTag: "sign-highload" ) << (sign | exponent | mantissaTop); |
| 249 | } |
| 250 | |
| 251 | template<typename F, typename Whole> |
| 252 | void tst_QNumeric::generalNaN() |
| 253 | { |
| 254 | Q_STATIC_ASSERT(sizeof(F) == sizeof(Whole)); |
| 255 | QFETCH(const Whole, whole); |
| 256 | F nan; |
| 257 | memcpy(&nan, &whole, sizeof(F)); |
| 258 | checkNaN(nan); |
| 259 | } |
| 260 | |
| 261 | template<typename F> |
| 262 | void tst_QNumeric::infinity() |
| 263 | { |
| 264 | const F inf = qInf(); |
| 265 | const F zero(0), one(1), two(2); |
| 266 | QVERIFY(inf > zero); |
| 267 | QVERIFY(-inf < zero); |
| 268 | QVERIFY(qIsInf(inf)); |
| 269 | QCOMPARE(inf, inf); |
| 270 | QCOMPARE(-inf, -inf); |
| 271 | QVERIFY(qIsInf(-inf)); |
| 272 | QVERIFY(qIsInf(inf + one)); |
| 273 | QVERIFY(qIsInf(inf - one)); |
| 274 | QVERIFY(qIsInf(-inf - one)); |
| 275 | QVERIFY(qIsInf(-inf + one)); |
| 276 | QVERIFY(qIsInf(inf * two)); |
| 277 | QVERIFY(qIsInf(-inf * two)); |
| 278 | QVERIFY(qIsInf(inf / two)); |
| 279 | QVERIFY(qIsInf(-inf / two)); |
| 280 | QVERIFY(qFuzzyCompare(one / inf, zero)); |
| 281 | QCOMPARE(1.0 / inf, 0.0); |
| 282 | QVERIFY(qFuzzyCompare(one / -inf, zero)); |
| 283 | QCOMPARE(one / -inf, zero); |
| 284 | QVERIFY(qIsNaN(zero * inf)); |
| 285 | QVERIFY(qIsNaN(zero * -inf)); |
| 286 | } |
| 287 | |
| 288 | template<typename F> |
| 289 | void tst_QNumeric::classifyfp() |
| 290 | { |
| 291 | using Bounds = std::numeric_limits<F>; |
| 292 | const F huge = Bounds::max(); |
| 293 | const F tiny = Bounds::min(); |
| 294 | // NaNs already handled, see checkNaN()'s callers. |
| 295 | const F one(1), two(2), inf(qInf()); |
| 296 | |
| 297 | QCOMPARE(qFpClassify(inf), FP_INFINITE); |
| 298 | QCOMPARE(qFpClassify(-inf), FP_INFINITE); |
| 299 | QCOMPARE(qFpClassify(huge * two), FP_INFINITE); |
| 300 | QCOMPARE(qFpClassify(huge * -two), FP_INFINITE); |
| 301 | |
| 302 | QCOMPARE(qFpClassify(one), FP_NORMAL); |
| 303 | QCOMPARE(qFpClassify(huge), FP_NORMAL); |
| 304 | QCOMPARE(qFpClassify(-huge), FP_NORMAL); |
| 305 | QCOMPARE(qFpClassify(tiny), FP_NORMAL); |
| 306 | QCOMPARE(qFpClassify(-tiny), FP_NORMAL); |
| 307 | if (Bounds::has_denorm == std::denorm_present) { |
| 308 | QCOMPARE(qFpClassify(tiny / two), FP_SUBNORMAL); |
| 309 | QCOMPARE(qFpClassify(tiny / -two), FP_SUBNORMAL); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | template<typename F, typename Count> |
| 314 | void tst_QNumeric::distance_data() |
| 315 | { |
| 316 | using Bounds = std::numeric_limits<F>; |
| 317 | const F huge = Bounds::max(); |
| 318 | const F tiny = Bounds::min(); |
| 319 | |
| 320 | QTest::addColumn<F>("from" ); |
| 321 | QTest::addColumn<F>("stop" ); |
| 322 | QTest::addColumn<Count>("expectedDistance" ); |
| 323 | |
| 324 | using Bounds = std::numeric_limits<F>; |
| 325 | const int mantissaBits = Bounds::digits - 1; |
| 326 | const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits; |
| 327 | |
| 328 | // Set to 1 and 0 if denormals are not included: |
| 329 | const Count count_0_to_tiny = Count(1) << mantissaBits; |
| 330 | const Count count_denormals = count_0_to_tiny - 1; |
| 331 | |
| 332 | // We need +1 to include the 0: |
| 333 | const Count count_0_to_1 |
| 334 | = (Count(1) << mantissaBits) * ((Count(1) << (exponentBits - 1)) - 2) |
| 335 | + 1 + count_denormals; |
| 336 | const Count count_1_to_2 = Count(1) << mantissaBits; |
| 337 | |
| 338 | // We don't need +1 because huge has all bits set in the mantissa. (Thus mantissa |
| 339 | // have not wrapped back to 0, which would be the case for 1 in _0_to_1 |
| 340 | const Count count_0_to_huge |
| 341 | = (Count(1) << mantissaBits) * ((Count(1) << exponentBits) - 2) |
| 342 | + count_denormals; |
| 343 | |
| 344 | const F zero(0), half(.5), one(1), sesqui(1.5), two(2); |
| 345 | const F denormal = tiny / two; |
| 346 | |
| 347 | QTest::newRow(dataTag: "[0,tiny]" ) << zero << tiny << count_0_to_tiny; |
| 348 | QTest::newRow(dataTag: "[0,huge]" ) << zero << huge << count_0_to_huge; |
| 349 | QTest::newRow(dataTag: "[1,1.5]" ) << one << sesqui << (Count(1) << (mantissaBits - 1)); |
| 350 | QTest::newRow(dataTag: "[0,1]" ) << zero << one << count_0_to_1; |
| 351 | QTest::newRow(dataTag: "[0.5,1]" ) << half << one << (Count(1) << mantissaBits); |
| 352 | QTest::newRow(dataTag: "[1,2]" ) << one << two << count_1_to_2; |
| 353 | QTest::newRow(dataTag: "[-1,+1]" ) << -one << +one << 2 * count_0_to_1; |
| 354 | QTest::newRow(dataTag: "[-1,0]" ) << -one << zero << count_0_to_1; |
| 355 | QTest::newRow(dataTag: "[-1,huge]" ) << -one << huge << count_0_to_1 + count_0_to_huge; |
| 356 | QTest::newRow(dataTag: "[-2,-1" ) << -two << -one << count_1_to_2; |
| 357 | QTest::newRow(dataTag: "[-1,-2" ) << -one << -two << count_1_to_2; |
| 358 | QTest::newRow(dataTag: "[tiny,huge]" ) << tiny << huge << count_0_to_huge - count_0_to_tiny; |
| 359 | QTest::newRow(dataTag: "[-huge,huge]" ) << -huge << huge << (2 * count_0_to_huge); |
| 360 | QTest::newRow(dataTag: "denormal" ) << zero << denormal << count_0_to_tiny / 2; |
| 361 | } |
| 362 | |
| 363 | template<typename F, typename Count> |
| 364 | void tst_QNumeric::distance() |
| 365 | { |
| 366 | QFETCH(F, from); |
| 367 | QFETCH(F, stop); |
| 368 | QFETCH(Count, expectedDistance); |
| 369 | #ifdef Q_OS_QNX |
| 370 | QEXPECT_FAIL("denormal" , "See QTBUG-37094" , Continue); |
| 371 | #endif |
| 372 | QCOMPARE(qFloatDistance(from, stop), expectedDistance); |
| 373 | } |
| 374 | |
| 375 | // Whole number tests: |
| 376 | |
| 377 | void tst_QNumeric::addOverflow_data() |
| 378 | { |
| 379 | QTest::addColumn<int>(name: "size" ); |
| 380 | |
| 381 | // for unsigned, all sizes are supported |
| 382 | QTest::newRow(dataTag: "quint8" ) << 8; |
| 383 | QTest::newRow(dataTag: "quint16" ) << 16; |
| 384 | QTest::newRow(dataTag: "quint32" ) << 32; |
| 385 | QTest::newRow(dataTag: "quint64" ) << 64; |
| 386 | QTest::newRow(dataTag: "ulong" ) << 48; // it's either 32- or 64-bit, so on average it's 48 :-) |
| 387 | |
| 388 | // for signed, we can't guarantee 64-bit |
| 389 | QTest::newRow(dataTag: "qint8" ) << -8; |
| 390 | QTest::newRow(dataTag: "qint16" ) << -16; |
| 391 | QTest::newRow(dataTag: "qint32" ) << -32; |
| 392 | if (sizeof(void *) == sizeof(qint64)) |
| 393 | QTest::newRow(dataTag: "qint64" ) << -64; |
| 394 | } |
| 395 | |
| 396 | // Note: in release mode, all the tests may be statically determined and only the calls |
| 397 | // to QTest::toString and QTest::qCompare will remain. |
| 398 | template <typename Int> static void addOverflow_template() |
| 399 | { |
| 400 | #if defined(Q_CC_MSVC) && Q_CC_MSVC < 2000 |
| 401 | QSKIP("Test disabled, this test generates an Internal Compiler Error compiling in release mode" ); |
| 402 | #else |
| 403 | const Int max = std::numeric_limits<Int>::max(); |
| 404 | const Int min = std::numeric_limits<Int>::min(); |
| 405 | Int r; |
| 406 | |
| 407 | // basic values |
| 408 | QCOMPARE(add_overflow(Int(0), Int(0), &r), false); |
| 409 | QCOMPARE(r, Int(0)); |
| 410 | QCOMPARE(add_overflow(Int(1), Int(0), &r), false); |
| 411 | QCOMPARE(r, Int(1)); |
| 412 | QCOMPARE(add_overflow(Int(0), Int(1), &r), false); |
| 413 | QCOMPARE(r, Int(1)); |
| 414 | |
| 415 | QCOMPARE(sub_overflow(Int(0), Int(0), &r), false); |
| 416 | QCOMPARE(r, Int(0)); |
| 417 | QCOMPARE(sub_overflow(Int(1), Int(0), &r), false); |
| 418 | QCOMPARE(r, Int(1)); |
| 419 | QCOMPARE(sub_overflow(Int(1), Int(1), &r), false); |
| 420 | QCOMPARE(r, Int(0)); |
| 421 | QCOMPARE(sub_overflow(Int(0), Int(1), &r), !min); |
| 422 | if (min) |
| 423 | QCOMPARE(r, Int(-1)); |
| 424 | |
| 425 | // half-way through max |
| 426 | QCOMPARE(add_overflow(Int(max/2), Int(max/2), &r), false); |
| 427 | QCOMPARE(r, Int(max / 2 * 2)); |
| 428 | QCOMPARE(sub_overflow(Int(max/2), Int(max/2), &r), false); |
| 429 | QCOMPARE(r, Int(0)); |
| 430 | QCOMPARE(add_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), false); |
| 431 | QCOMPARE(r, Int(max / 2 * 2)); |
| 432 | QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), !min); |
| 433 | if (min) |
| 434 | QCOMPARE(r, Int(-2)); |
| 435 | QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2), &r), false); |
| 436 | QCOMPARE(r, max); |
| 437 | QCOMPARE(sub_overflow(Int(max/2 + 1), Int(max/2), &r), false); |
| 438 | QCOMPARE(r, Int(1)); |
| 439 | QCOMPARE(add_overflow(Int(max/2), Int(max/2 + 1), &r), false); |
| 440 | QCOMPARE(r, max); |
| 441 | QCOMPARE(sub_overflow(Int(max/2), Int(max/2 + 1), &r), !min); |
| 442 | if (min) |
| 443 | QCOMPARE(r, Int(-1)); |
| 444 | |
| 445 | QCOMPARE(add_overflow(Int(min/2), Int(min/2), &r), false); |
| 446 | QCOMPARE(r, Int(min / 2 * 2)); |
| 447 | QCOMPARE(sub_overflow(Int(min/2), Int(min/2), &r), false); |
| 448 | QCOMPARE(r, Int(0)); |
| 449 | QCOMPARE(add_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), !min); |
| 450 | if (min) |
| 451 | QCOMPARE(r, Int(min / 2 * 2)); |
| 452 | QCOMPARE(sub_overflow(Int(min/2 - 1), Int(min/2 + 1), &r), false); |
| 453 | QCOMPARE(r, Int(-2)); |
| 454 | QCOMPARE(sub_overflow(Int(min/2 + 1), Int(min/2), &r), false); |
| 455 | QCOMPARE(r, Int(1)); |
| 456 | QCOMPARE(sub_overflow(Int(min/2), Int(min/2 + 1), &r), !min); |
| 457 | if (min) |
| 458 | QCOMPARE(r, Int(-1)); |
| 459 | |
| 460 | // more than half |
| 461 | QCOMPARE(add_overflow(Int(max/4 * 3), Int(max/4), &r), false); |
| 462 | QCOMPARE(r, Int(max / 4 * 4)); |
| 463 | |
| 464 | // max |
| 465 | QCOMPARE(add_overflow(max, Int(0), &r), false); |
| 466 | QCOMPARE(r, max); |
| 467 | QCOMPARE(sub_overflow(max, Int(0), &r), false); |
| 468 | QCOMPARE(r, max); |
| 469 | QCOMPARE(add_overflow(Int(0), max, &r), false); |
| 470 | QCOMPARE(r, max); |
| 471 | QCOMPARE(sub_overflow(Int(0), max, &r), !min); |
| 472 | if (min) |
| 473 | QCOMPARE(r, Int(-max)); |
| 474 | |
| 475 | QCOMPARE(add_overflow(min, Int(0), &r), false); |
| 476 | QCOMPARE(r, min); |
| 477 | QCOMPARE(sub_overflow(min, Int(0), &r), false); |
| 478 | QCOMPARE(r, min); |
| 479 | QCOMPARE(add_overflow(Int(0), min, &r), false); |
| 480 | QCOMPARE(r, min); |
| 481 | QCOMPARE(sub_overflow(Int(0), Int(min+1), &r), !min); |
| 482 | if (min) |
| 483 | QCOMPARE(r, Int(-(min+1))); |
| 484 | |
| 485 | // 64-bit issues |
| 486 | if (max > std::numeric_limits<uint>::max()) { |
| 487 | QCOMPARE(add_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false); |
| 488 | QCOMPARE(r, Int(2 * Int(std::numeric_limits<uint>::max()))); |
| 489 | QCOMPARE(sub_overflow(Int(std::numeric_limits<uint>::max()), Int(std::numeric_limits<uint>::max()), &r), false); |
| 490 | QCOMPARE(r, Int(0)); |
| 491 | } |
| 492 | if (min && min < -Int(std::numeric_limits<uint>::max())) { |
| 493 | QCOMPARE(add_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false); |
| 494 | QCOMPARE(r, Int(-2 * Int(std::numeric_limits<uint>::max()))); |
| 495 | QCOMPARE(sub_overflow(Int(-Int(std::numeric_limits<uint>::max())), Int(-Int(std::numeric_limits<uint>::max())), &r), false); |
| 496 | QCOMPARE(r, Int(0)); |
| 497 | } |
| 498 | |
| 499 | // overflows past max |
| 500 | QCOMPARE(add_overflow(max, Int(1), &r), true); |
| 501 | QCOMPARE(add_overflow(Int(1), max, &r), true); |
| 502 | QCOMPARE(add_overflow(Int(max/2 + 1), Int(max/2 + 1), &r), true); |
| 503 | if (!min) { |
| 504 | QCOMPARE(sub_overflow(Int(-max), Int(-2), &r), true); |
| 505 | QCOMPARE(sub_overflow(Int(max/2 - 1), Int(max/2 + 1), &r), true); |
| 506 | } |
| 507 | |
| 508 | // overflows past min (in case of min == 0, repeats some tests above) |
| 509 | if (min) { |
| 510 | QCOMPARE(sub_overflow(min, Int(1), &r), true); |
| 511 | QCOMPARE(sub_overflow(Int(1), min, &r), true); |
| 512 | QCOMPARE(sub_overflow(Int(min/2 - 1), Int(-Int(min/2)), &r), true); |
| 513 | QCOMPARE(add_overflow(min, Int(-1), &r), true); |
| 514 | QCOMPARE(add_overflow(Int(-1), min, &r), true); |
| 515 | } |
| 516 | #endif |
| 517 | } |
| 518 | |
| 519 | void tst_QNumeric::addOverflow() |
| 520 | { |
| 521 | QFETCH(int, size); |
| 522 | if (size == 8) |
| 523 | addOverflow_template<quint8>(); |
| 524 | if (size == 16) |
| 525 | addOverflow_template<quint16>(); |
| 526 | if (size == 32) |
| 527 | addOverflow_template<quint32>(); |
| 528 | if (size == 48) |
| 529 | addOverflow_template<ulong>(); // not really 48-bit |
| 530 | if (size == 64) |
| 531 | addOverflow_template<quint64>(); |
| 532 | |
| 533 | if (size == -8) |
| 534 | addOverflow_template<qint8>(); |
| 535 | if (size == -16) |
| 536 | addOverflow_template<qint16>(); |
| 537 | if (size == -32) |
| 538 | addOverflow_template<qint32>(); |
| 539 | if (size == -64) |
| 540 | addOverflow_template<qint64>(); |
| 541 | } |
| 542 | |
| 543 | void tst_QNumeric::mulOverflow_data() |
| 544 | { |
| 545 | addOverflow_data(); |
| 546 | } |
| 547 | |
| 548 | // Note: in release mode, all the tests may be statically determined and only the calls |
| 549 | // to QTest::toString and QTest::qCompare will remain. |
| 550 | template <typename Int> static void mulOverflow_template() |
| 551 | { |
| 552 | #if defined(Q_CC_MSVC) && Q_CC_MSVC < 1900 |
| 553 | QSKIP("Test disabled, this test generates an Internal Compiler Error compiling" ); |
| 554 | #else |
| 555 | const Int max = std::numeric_limits<Int>::max(); |
| 556 | const Int min = std::numeric_limits<Int>::min(); |
| 557 | |
| 558 | // for unsigned (even number of significant bits): mid2 = mid1 - 1 |
| 559 | // for signed (odd number of significant bits): mid2 = mid1 / 2 - 1 |
| 560 | const Int mid1 = Int(Int(1) << sizeof(Int) * CHAR_BIT / 2); |
| 561 | const Int mid2 = (std::numeric_limits<Int>::digits % 2 ? mid1 / 2 : mid1) - 1; |
| 562 | |
| 563 | Int r; |
| 564 | |
| 565 | // basic multiplications |
| 566 | QCOMPARE(mul_overflow(Int(0), Int(0), &r), false); |
| 567 | QCOMPARE(r, Int(0)); |
| 568 | QCOMPARE(mul_overflow(Int(1), Int(0), &r), false); |
| 569 | QCOMPARE(r, Int(0)); |
| 570 | QCOMPARE(mul_overflow(Int(0), Int(1), &r), false); |
| 571 | QCOMPARE(r, Int(0)); |
| 572 | QCOMPARE(mul_overflow(max, Int(0), &r), false); |
| 573 | QCOMPARE(r, Int(0)); |
| 574 | QCOMPARE(mul_overflow(Int(0), max, &r), false); |
| 575 | QCOMPARE(r, Int(0)); |
| 576 | QCOMPARE(mul_overflow(min, Int(0), &r), false); |
| 577 | QCOMPARE(r, Int(0)); |
| 578 | QCOMPARE(mul_overflow(Int(0), min, &r), false); |
| 579 | QCOMPARE(r, Int(0)); |
| 580 | |
| 581 | QCOMPARE(mul_overflow(Int(1), Int(1), &r), false); |
| 582 | QCOMPARE(r, Int(1)); |
| 583 | QCOMPARE(mul_overflow(Int(1), max, &r), false); |
| 584 | QCOMPARE(r, max); |
| 585 | QCOMPARE(mul_overflow(max, Int(1), &r), false); |
| 586 | QCOMPARE(r, max); |
| 587 | QCOMPARE(mul_overflow(Int(1), min, &r), false); |
| 588 | QCOMPARE(r, min); |
| 589 | QCOMPARE(mul_overflow(min, Int(1), &r), false); |
| 590 | QCOMPARE(r, min); |
| 591 | |
| 592 | // almost max |
| 593 | QCOMPARE(mul_overflow(mid1, mid2, &r), false); |
| 594 | QCOMPARE(r, Int(max - mid1 + 1)); |
| 595 | QCOMPARE(mul_overflow(Int(max / 2), Int(2), &r), false); |
| 596 | QCOMPARE(r, Int(max & ~Int(1))); |
| 597 | QCOMPARE(mul_overflow(Int(max / 4), Int(4), &r), false); |
| 598 | QCOMPARE(r, Int(max & ~Int(3))); |
| 599 | if (min) { |
| 600 | QCOMPARE(mul_overflow(Int(-mid1), mid2, &r), false); |
| 601 | QCOMPARE(r, Int(-max + mid1 - 1)); |
| 602 | QCOMPARE(mul_overflow(Int(-max / 2), Int(2), &r), false); |
| 603 | QCOMPARE(r, Int(-max + 1)); |
| 604 | QCOMPARE(mul_overflow(Int(-max / 4), Int(4), &r), false); |
| 605 | QCOMPARE(r, Int(-max + 3)); |
| 606 | |
| 607 | QCOMPARE(mul_overflow(Int(-mid1), Int(mid2 + 1), &r), false); |
| 608 | QCOMPARE(r, min); |
| 609 | QCOMPARE(mul_overflow(mid1, Int(-mid2 - 1), &r), false); |
| 610 | QCOMPARE(r, min); |
| 611 | } |
| 612 | |
| 613 | // overflows |
| 614 | QCOMPARE(mul_overflow(max, Int(2), &r), true); |
| 615 | QCOMPARE(mul_overflow(Int(max / 2), Int(3), &r), true); |
| 616 | QCOMPARE(mul_overflow(mid1, Int(mid2 + 1), &r), true); |
| 617 | QCOMPARE(mul_overflow(Int(max / 2 + 2), Int(2), &r), true); |
| 618 | QCOMPARE(mul_overflow(Int(max - max / 2), Int(2), &r), true); |
| 619 | QCOMPARE(mul_overflow(Int(1ULL << (std::numeric_limits<Int>::digits - 1)), Int(2), &r), true); |
| 620 | |
| 621 | if (min) { |
| 622 | QCOMPARE(mul_overflow(min, Int(2), &r), true); |
| 623 | QCOMPARE(mul_overflow(Int(min / 2), Int(3), &r), true); |
| 624 | QCOMPARE(mul_overflow(Int(min / 2 - 1), Int(2), &r), true); |
| 625 | } |
| 626 | #endif |
| 627 | } |
| 628 | |
| 629 | template <typename Int, bool enabled = sizeof(Int) <= sizeof(void*)> struct MulOverflowDispatch; |
| 630 | template <typename Int> struct MulOverflowDispatch<Int, true> |
| 631 | { |
| 632 | void operator()() { mulOverflow_template<Int>(); } |
| 633 | }; |
| 634 | template <typename Int> struct MulOverflowDispatch<Int, false> |
| 635 | { |
| 636 | void operator()() { QSKIP("This type is too big for this architecture" ); } |
| 637 | }; |
| 638 | |
| 639 | void tst_QNumeric::mulOverflow() |
| 640 | { |
| 641 | QFETCH(int, size); |
| 642 | if (size == 8) |
| 643 | MulOverflowDispatch<quint8>()(); |
| 644 | if (size == 16) |
| 645 | MulOverflowDispatch<quint16>()(); |
| 646 | if (size == 32) |
| 647 | MulOverflowDispatch<quint32>()(); |
| 648 | if (size == 48) |
| 649 | MulOverflowDispatch<ulong>()(); // not really 48-bit |
| 650 | if (size == 64) |
| 651 | MulOverflowDispatch<quint64>()(); |
| 652 | |
| 653 | if (size == -8) |
| 654 | MulOverflowDispatch<qint8>()(); |
| 655 | if (size == -16) |
| 656 | MulOverflowDispatch<qint16>()(); |
| 657 | if (size == -32) |
| 658 | MulOverflowDispatch<qint32>()(); |
| 659 | if (size == -64) { |
| 660 | #if QT_POINTER_SIZE == 8 |
| 661 | MulOverflowDispatch<qint64>()(); |
| 662 | #else |
| 663 | QFAIL("128-bit multiplication not supported on this platform" ); |
| 664 | #endif |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | void tst_QNumeric::signedOverflow() |
| 669 | { |
| 670 | const int minInt = std::numeric_limits<int>::min(); |
| 671 | const int maxInt = std::numeric_limits<int>::max(); |
| 672 | int r; |
| 673 | |
| 674 | QCOMPARE(add_overflow(minInt + 1, int(-1), &r), false); |
| 675 | QCOMPARE(add_overflow(minInt, int(-1), &r), true); |
| 676 | QCOMPARE(add_overflow(minInt, minInt, &r), true); |
| 677 | QCOMPARE(add_overflow(maxInt - 1, int(1), &r), false); |
| 678 | QCOMPARE(add_overflow(maxInt, int(1), &r), true); |
| 679 | QCOMPARE(add_overflow(maxInt, maxInt, &r), true); |
| 680 | |
| 681 | QCOMPARE(sub_overflow(minInt + 1, int(1), &r), false); |
| 682 | QCOMPARE(sub_overflow(minInt, int(1), &r), true); |
| 683 | QCOMPARE(sub_overflow(minInt, maxInt, &r), true); |
| 684 | QCOMPARE(sub_overflow(maxInt - 1, int(-1), &r), false); |
| 685 | QCOMPARE(sub_overflow(maxInt, int(-1), &r), true); |
| 686 | QCOMPARE(sub_overflow(maxInt, minInt, &r), true); |
| 687 | |
| 688 | QCOMPARE(mul_overflow(minInt, int(1), &r), false); |
| 689 | QCOMPARE(mul_overflow(minInt, int(-1), &r), true); |
| 690 | QCOMPARE(mul_overflow(minInt, int(2), &r), true); |
| 691 | QCOMPARE(mul_overflow(minInt, minInt, &r), true); |
| 692 | QCOMPARE(mul_overflow(maxInt, int(1), &r), false); |
| 693 | QCOMPARE(mul_overflow(maxInt, int(-1), &r), false); |
| 694 | QCOMPARE(mul_overflow(maxInt, int(2), &r), true); |
| 695 | QCOMPARE(mul_overflow(maxInt, maxInt, &r), true); |
| 696 | } |
| 697 | |
| 698 | QTEST_APPLESS_MAIN(tst_QNumeric) |
| 699 | #include "tst_qnumeric.moc" |
| 700 | |