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 | |