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
38namespace {
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
48class 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
68private 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
105template<typename F>
106void 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
133template<typename F>
134void 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
151template<typename F>
152void 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
185template<typename F>
186void 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
199template<typename F>
200void 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)
212template<typename F>
213void tst_QNumeric::distinctNaN()
214{
215 const F qnan = qQNaN();
216 const F snan = qSNaN();
217 QVERIFY(memcmp(&qnan, &snan, sizeof(F)) != 0);
218}
219
220void 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
228template<typename F, typename Whole>
229void 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
251template<typename F, typename Whole>
252void 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
261template<typename F>
262void 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
288template<typename F>
289void 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
313template<typename F, typename Count>
314void 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
363template<typename F, typename Count>
364void 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
377void 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.
398template <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
519void 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
543void 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.
550template <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
629template <typename Int, bool enabled = sizeof(Int) <= sizeof(void*)> struct MulOverflowDispatch;
630template <typename Int> struct MulOverflowDispatch<Int, true>
631{
632 void operator()() { mulOverflow_template<Int>(); }
633};
634template <typename Int> struct MulOverflowDispatch<Int, false>
635{
636 void operator()() { QSKIP("This type is too big for this architecture"); }
637};
638
639void 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
668void 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
698QTEST_APPLESS_MAIN(tst_QNumeric)
699#include "tst_qnumeric.moc"
700

source code of qtbase/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp