1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 by Southwest Research Institute (R) |
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 | #include <QtTest/QtTest> |
31 | #include <QFloat16> |
32 | |
33 | #include <math.h> |
34 | |
35 | class tst_qfloat16: public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | private slots: |
40 | void fuzzyCompare_data(); |
41 | void fuzzyCompare(); |
42 | void ltgt_data(); |
43 | void ltgt(); |
44 | void qNaN(); |
45 | void infinity(); |
46 | void float_cast(); |
47 | void float_cast_data(); |
48 | void promotionTests(); |
49 | void arithOps_data(); |
50 | void arithOps(); |
51 | void floatToFloat16(); |
52 | void floatFromFloat16(); |
53 | void finite_data(); |
54 | void finite(); |
55 | void properties(); |
56 | void limits(); |
57 | }; |
58 | |
59 | void tst_qfloat16::fuzzyCompare_data() |
60 | { |
61 | QTest::addColumn<qfloat16>(name: "val1" ); |
62 | QTest::addColumn<qfloat16>(name: "val2" ); |
63 | QTest::addColumn<bool>(name: "fuzEqual" ); |
64 | QTest::addColumn<bool>(name: "isEqual" ); |
65 | |
66 | QTest::newRow(dataTag: "zero" ) << qfloat16(0.0f) << qfloat16(0.0f) << true << true; |
67 | QTest::newRow(dataTag: "ten" ) << qfloat16(1e1f) << qfloat16(1e1f) << true << true; |
68 | QTest::newRow(dataTag: "large" ) << qfloat16(1e4f) << qfloat16(1e4f) << true << true; |
69 | QTest::newRow(dataTag: "small" ) << qfloat16(1e-5f) << qfloat16(1e-5f) << true << true; |
70 | QTest::newRow(dataTag: "eps" ) << qfloat16(10.01f) << qfloat16(10.02f) << true << false; |
71 | QTest::newRow(dataTag: "eps2" ) << qfloat16(1024.f) << qfloat16(1033.f) << true << false; |
72 | |
73 | QTest::newRow(dataTag: "mis1" ) << qfloat16(0.0f) << qfloat16(1.0f) << false << false; |
74 | QTest::newRow(dataTag: "mis2" ) << qfloat16(0.0f) << qfloat16(1e7f) << false << false; |
75 | QTest::newRow(dataTag: "mis3" ) << qfloat16(0.0f) << qfloat16(1e-4f) << false << false; |
76 | QTest::newRow(dataTag: "mis4" ) << qfloat16(1e8f) << qfloat16(1e-8f) << false << false; |
77 | QTest::newRow(dataTag: "mis5" ) << qfloat16(1e-4f) << qfloat16(1e-5) << false << false; |
78 | QTest::newRow(dataTag: "mis6" ) << qfloat16(1024.f) << qfloat16(1034.f) << false << false; |
79 | } |
80 | |
81 | void tst_qfloat16::fuzzyCompare() |
82 | { |
83 | QFETCH(qfloat16, val1); |
84 | QFETCH(qfloat16, val2); |
85 | QFETCH(bool, fuzEqual); |
86 | QFETCH(bool, isEqual); |
87 | |
88 | if (!isEqual && (val1==val2)) |
89 | qWarning() << "Identical arguments provided unintentionally!" ; |
90 | |
91 | if (fuzEqual) { |
92 | QVERIFY(::qFuzzyCompare(val1, val2)); |
93 | QVERIFY(::qFuzzyCompare(val2, val1)); |
94 | QVERIFY(::qFuzzyCompare(-val1, -val2)); |
95 | QVERIFY(::qFuzzyCompare(-val2, -val1)); |
96 | } else { |
97 | QVERIFY(!::qFuzzyCompare(val1, val2)); |
98 | QVERIFY(!::qFuzzyCompare(val2, val1)); |
99 | QVERIFY(!::qFuzzyCompare(-val1, -val2)); |
100 | QVERIFY(!::qFuzzyCompare(-val2, -val1)); |
101 | } |
102 | } |
103 | |
104 | void tst_qfloat16::ltgt_data() |
105 | { |
106 | QTest::addColumn<float>(name: "val1" ); |
107 | QTest::addColumn<float>(name: "val2" ); |
108 | |
109 | QTest::newRow(dataTag: "zero" ) << 0.0f << 0.0f; |
110 | QTest::newRow(dataTag: "-zero" ) << -0.0f << 0.0f; |
111 | QTest::newRow(dataTag: "ten" ) << 10.0f << 10.0f; |
112 | QTest::newRow(dataTag: "large" ) << 100000.0f << 100000.0f; |
113 | QTest::newRow(dataTag: "small" ) << 0.0000001f << 0.0000001f; |
114 | QTest::newRow(dataTag: "eps" ) << 10.000000000000001f << 10.00000000000002f; |
115 | QTest::newRow(dataTag: "eps2" ) << 10.000000000000001f << 10.000000000000009f; |
116 | |
117 | QTest::newRow(dataTag: "mis1" ) << 0.0f << 1.0f; |
118 | QTest::newRow(dataTag: "mis2" ) << 0.0f << 10000000.0f; |
119 | QTest::newRow(dataTag: "mis3" ) << 0.0f << 0.0001f; |
120 | QTest::newRow(dataTag: "mis4" ) << 100000000.0f << 0.000000001f; |
121 | QTest::newRow(dataTag: "mis5" ) << 0.0001f << 0.00001f; |
122 | |
123 | QTest::newRow(dataTag: "45,23" ) << 45.f << 23.f; |
124 | QTest::newRow(dataTag: "1000,76" ) << 1000.f << 76.f; |
125 | } |
126 | |
127 | void tst_qfloat16::ltgt() |
128 | { |
129 | QFETCH(float, val1); |
130 | QFETCH(float, val2); |
131 | |
132 | QCOMPARE(qfloat16(val1) == qfloat16(val2), val1 == val2); |
133 | QCOMPARE(qfloat16(val1) < qfloat16(val2), val1 < val2); |
134 | QCOMPARE(qfloat16(val1) <= qfloat16(val2), val1 <= val2); |
135 | QCOMPARE(qfloat16(val1) > qfloat16(val2), val1 > val2); |
136 | QCOMPARE(qfloat16(val1) >= qfloat16(val2), val1 >= val2); |
137 | |
138 | QCOMPARE(qfloat16(val1) == qfloat16(-val2), val1 == -val2); |
139 | QCOMPARE(qfloat16(val1) < qfloat16(-val2), val1 < -val2); |
140 | QCOMPARE(qfloat16(val1) <= qfloat16(-val2), val1 <= -val2); |
141 | QCOMPARE(qfloat16(val1) > qfloat16(-val2), val1 > -val2); |
142 | QCOMPARE(qfloat16(val1) >= qfloat16(-val2), val1 >= -val2); |
143 | |
144 | QCOMPARE(qfloat16(-val1) == qfloat16(val2), -val1 == val2); |
145 | QCOMPARE(qfloat16(-val1) < qfloat16(val2), -val1 < val2); |
146 | QCOMPARE(qfloat16(-val1) <= qfloat16(val2), -val1 <= val2); |
147 | QCOMPARE(qfloat16(-val1) > qfloat16(val2), -val1 > val2); |
148 | QCOMPARE(qfloat16(-val1) >= qfloat16(val2), -val1 >= val2); |
149 | |
150 | QCOMPARE(qfloat16(-val1) == qfloat16(-val2), -val1 == -val2); |
151 | QCOMPARE(qfloat16(-val1) < qfloat16(-val2), -val1 < -val2); |
152 | QCOMPARE(qfloat16(-val1) <= qfloat16(-val2), -val1 <= -val2); |
153 | QCOMPARE(qfloat16(-val1) > qfloat16(-val2), -val1 > -val2); |
154 | QCOMPARE(qfloat16(-val1) >= qfloat16(-val2), -val1 >= -val2); |
155 | } |
156 | |
157 | #if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) |
158 | // turn -ffast-math off |
159 | # pragma GCC optimize "no-fast-math" |
160 | #endif |
161 | |
162 | void tst_qfloat16::qNaN() |
163 | { |
164 | #if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404) |
165 | QSKIP("Non-conformant fast math mode is enabled, cannot run test" ); |
166 | #endif |
167 | using Bounds = std::numeric_limits<qfloat16>; |
168 | const qfloat16 nan = Bounds::quiet_NaN(); |
169 | const qfloat16 zero(0), one(1); |
170 | QVERIFY(!(zero > nan)); |
171 | QVERIFY(!(zero < nan)); |
172 | QVERIFY(!(zero == nan)); |
173 | QVERIFY(!qIsInf(nan)); |
174 | QVERIFY(qIsNaN(nan)); |
175 | QVERIFY(qIsNaN(nan + one)); |
176 | QVERIFY(qIsNaN(-nan)); |
177 | #ifdef Q_CC_INTEL |
178 | QEXPECT_FAIL("" , "ICC optimizes zero * anything to zero" , Continue); |
179 | #endif |
180 | QVERIFY(qIsNaN(nan * zero)); |
181 | #ifdef Q_CC_INTEL |
182 | QEXPECT_FAIL("" , "ICC optimizes zero * anything to zero" , Continue); |
183 | #endif |
184 | QVERIFY(qIsNaN(Bounds::infinity() * zero)); |
185 | |
186 | QVERIFY(!nan.isNormal()); |
187 | QVERIFY(!qIsFinite(nan)); |
188 | QVERIFY(!(nan == nan)); |
189 | QCOMPARE(nan, nan); // Despite the preceding |
190 | QCOMPARE(qFpClassify(nan), FP_NAN); |
191 | } |
192 | |
193 | void tst_qfloat16::infinity() |
194 | { |
195 | const qfloat16 huge = std::numeric_limits<qfloat16>::infinity(); |
196 | const qfloat16 zero(0), one(1), two(2); |
197 | QVERIFY(huge > -huge); |
198 | QVERIFY(huge > zero); |
199 | QVERIFY(-huge < zero); |
200 | QCOMPARE(huge, huge); |
201 | QCOMPARE(-huge, -huge); |
202 | |
203 | // QTBUG-75812 - see overOptimized in the limits() test. |
204 | if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f)) { |
205 | QCOMPARE(one / huge, zero); |
206 | QVERIFY(qFuzzyCompare(one / huge, zero)); // (same thing) |
207 | } |
208 | |
209 | QVERIFY(qIsInf(huge)); |
210 | QVERIFY(qIsInf(-huge)); |
211 | QVERIFY(qIsInf(two * huge)); |
212 | QVERIFY(qIsInf(huge * two)); |
213 | |
214 | QVERIFY(!huge.isNormal()); |
215 | QVERIFY(!(-huge).isNormal()); |
216 | QVERIFY(!qIsNaN(huge)); |
217 | QVERIFY(!qIsNaN(-huge)); |
218 | QVERIFY(!qIsFinite(huge)); |
219 | QVERIFY(!qIsFinite(-huge)); |
220 | QCOMPARE(qFpClassify(huge), FP_INFINITE); |
221 | QCOMPARE(qFpClassify(-huge), FP_INFINITE); |
222 | } |
223 | |
224 | void tst_qfloat16::float_cast_data() |
225 | { |
226 | QTest::addColumn<float>(name: "val" ); |
227 | |
228 | QTest::newRow(dataTag: "zero" ) << 0.f; |
229 | QTest::newRow(dataTag: "one" ) << 1e0f; |
230 | QTest::newRow(dataTag: "ten" ) << 1e1f; |
231 | QTest::newRow(dataTag: "hund" ) << 1e2f; |
232 | QTest::newRow(dataTag: "thou" ) << 1e3f; |
233 | QTest::newRow(dataTag: "tthou" ) << 1e4f; |
234 | //QTest::newRow("hthou") << 1e5f; |
235 | //QTest::newRow("mil") << 1e6f; |
236 | //QTest::newRow("tmil") << 1e7f; |
237 | //QTest::newRow("hmil") << 1e8f; |
238 | } |
239 | |
240 | void tst_qfloat16::float_cast() |
241 | { |
242 | QFETCH(float, val); |
243 | |
244 | QVERIFY(qFuzzyCompare((float)(qfloat16(val)),val)); |
245 | QVERIFY(qFuzzyCompare((float)(qfloat16(-val)),-val)); |
246 | QVERIFY(qFuzzyCompare((double)(qfloat16(val)),(double)(val))); |
247 | QVERIFY(qFuzzyCompare((double)(qfloat16(-val)),(double)(-val))); |
248 | //QVERIFY(qFuzzyCompare((long double)(qfloat16(val)),(long double)(val))); |
249 | //QVERIFY(qFuzzyCompare((long double)(qfloat16(-val)),(long double)(-val))); |
250 | } |
251 | |
252 | void tst_qfloat16::promotionTests() |
253 | { |
254 | QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)+qfloat16(1.f))); |
255 | QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)-qfloat16(1.f))); |
256 | QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)*qfloat16(1.f))); |
257 | QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)/qfloat16(1.f))); |
258 | |
259 | QCOMPARE(sizeof(float),sizeof(1.f+qfloat16(1.f))); |
260 | QCOMPARE(sizeof(float),sizeof(1.f-qfloat16(1.f))); |
261 | QCOMPARE(sizeof(float),sizeof(1.f*qfloat16(1.f))); |
262 | QCOMPARE(sizeof(float),sizeof(1.f/qfloat16(1.f))); |
263 | |
264 | QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)+1.f)); |
265 | QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)-1.f)); |
266 | QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)*1.f)); |
267 | QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)/1.f)); |
268 | |
269 | QCOMPARE(sizeof(double),sizeof(1.+qfloat16(1.f))); |
270 | QCOMPARE(sizeof(double),sizeof(1.-qfloat16(1.f))); |
271 | QCOMPARE(sizeof(double),sizeof(1.*qfloat16(1.f))); |
272 | QCOMPARE(sizeof(double),sizeof(1./qfloat16(1.f))); |
273 | |
274 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)+1.)); |
275 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)-1.)); |
276 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)*1.)); |
277 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)/1.)); |
278 | |
279 | QCOMPARE(sizeof(long double),sizeof((long double)(1.)+qfloat16(1.f))); |
280 | QCOMPARE(sizeof(long double),sizeof((long double)(1.)-qfloat16(1.f))); |
281 | QCOMPARE(sizeof(long double),sizeof((long double)(1.)*qfloat16(1.f))); |
282 | QCOMPARE(sizeof(long double),sizeof((long double)(1.)/qfloat16(1.f))); |
283 | |
284 | QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)+(long double)(1.))); |
285 | QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)-(long double)(1.))); |
286 | QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)*(long double)(1.))); |
287 | QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)/(long double)(1.))); |
288 | |
289 | QCOMPARE(sizeof(double),sizeof(1+qfloat16(1.f))); |
290 | QCOMPARE(sizeof(double),sizeof(1-qfloat16(1.f))); |
291 | QCOMPARE(sizeof(double),sizeof(1*qfloat16(1.f))); |
292 | QCOMPARE(sizeof(double),sizeof(1/qfloat16(1.f))); |
293 | |
294 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)+1)); |
295 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)-1)); |
296 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)*1)); |
297 | QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)/1)); |
298 | |
299 | QCOMPARE(QString::number(1.f),QString::number(qfloat16(1.f))); |
300 | } |
301 | |
302 | void tst_qfloat16::arithOps_data() |
303 | { |
304 | QTest::addColumn<float>(name: "val1" ); |
305 | QTest::addColumn<float>(name: "val2" ); |
306 | |
307 | QTest::newRow(dataTag: "zero" ) << 0.0f << 2.0f; |
308 | QTest::newRow(dataTag: "one" ) << 1.0f << 4.0f; |
309 | QTest::newRow(dataTag: "ten" ) << 10.0f << 20.0f; |
310 | } |
311 | |
312 | void tst_qfloat16::arithOps() |
313 | { |
314 | QFETCH(float, val1); |
315 | QFETCH(float, val2); |
316 | |
317 | QVERIFY(qFuzzyCompare(float(qfloat16(val1) + qfloat16(val2)), val1 + val2)); |
318 | QVERIFY(qFuzzyCompare(float(qfloat16(val1) - qfloat16(val2)), val1 - val2)); |
319 | QVERIFY(qFuzzyCompare(float(qfloat16(val1) * qfloat16(val2)), val1 * val2)); |
320 | QVERIFY(qFuzzyCompare(float(qfloat16(val1) / qfloat16(val2)), val1 / val2)); |
321 | |
322 | QVERIFY(qFuzzyCompare(qfloat16(val1) + val2, val1 + val2)); |
323 | QVERIFY(qFuzzyCompare(qfloat16(val1) - val2, val1 - val2)); |
324 | QVERIFY(qFuzzyCompare(qfloat16(val1) * val2, val1 * val2)); |
325 | QVERIFY(qFuzzyCompare(qfloat16(val1) / val2, val1 / val2)); |
326 | |
327 | QVERIFY(qFuzzyCompare(val1 + qfloat16(val2), val1 + val2)); |
328 | QVERIFY(qFuzzyCompare(val1 - qfloat16(val2), val1 - val2)); |
329 | QVERIFY(qFuzzyCompare(val1 * qfloat16(val2), val1 * val2)); |
330 | QVERIFY(qFuzzyCompare(val1 / qfloat16(val2), val1 / val2)); |
331 | |
332 | float r1 = 0.f; |
333 | r1 += qfloat16(val2); |
334 | QVERIFY(qFuzzyCompare(r1,val2)); |
335 | |
336 | float r2 = 0.f; |
337 | r2 -= qfloat16(val2); |
338 | QVERIFY(qFuzzyCompare(r2,-val2)); |
339 | |
340 | float r3 = 1.f; |
341 | r3 *= qfloat16(val2); |
342 | QVERIFY(qFuzzyCompare(r3,val2)); |
343 | |
344 | float r4 = 1.f; |
345 | r4 /= qfloat16(val2); |
346 | QVERIFY(qFuzzyCompare(r4,1.f/val2)); |
347 | } |
348 | |
349 | void tst_qfloat16::floatToFloat16() |
350 | { |
351 | float in[63]; |
352 | qfloat16 out[63]; |
353 | qfloat16 expected[63]; |
354 | |
355 | for (int i = 0; i < 63; ++i) |
356 | in[i] = i * (1/13.f); |
357 | |
358 | for (int i = 0; i < 63; ++i) |
359 | expected[i] = qfloat16(in[i]); |
360 | |
361 | qFloatToFloat16(out, in, length: 63); |
362 | |
363 | for (int i = 0; i < 63; ++i) |
364 | QVERIFY(qFuzzyCompare(out[i], expected[i])); |
365 | } |
366 | |
367 | void tst_qfloat16::floatFromFloat16() |
368 | { |
369 | qfloat16 in[35]; |
370 | float out[35]; |
371 | float expected[35]; |
372 | |
373 | for (int i = 0; i < 35; ++i) |
374 | in[i] = qfloat16(i * (17.f / 3)); |
375 | |
376 | for (int i = 0; i < 35; ++i) |
377 | expected[i] = float(in[i]); |
378 | |
379 | qFloatFromFloat16(out, in, length: 35); |
380 | |
381 | for (int i = 0; i < 35; ++i) |
382 | QCOMPARE(out[i], expected[i]); |
383 | } |
384 | |
385 | static qfloat16 powf16(qfloat16 base, int raise) |
386 | { |
387 | const qfloat16 one(1.f); |
388 | if (raise < 0) { |
389 | raise = -raise; |
390 | base = one / base; |
391 | } |
392 | qfloat16 answer = (raise & 1) ? base : one; |
393 | while (raise > 0) { |
394 | raise >>= 1; |
395 | base *= base; |
396 | if (raise & 1) |
397 | answer *= base; |
398 | } |
399 | return answer; |
400 | } |
401 | |
402 | void tst_qfloat16::finite_data() |
403 | { |
404 | using Bounds = std::numeric_limits<qfloat16>; |
405 | QTest::addColumn<qfloat16>(name: "value" ); |
406 | QTest::addColumn<int>(name: "mode" ); |
407 | |
408 | QTest::newRow(dataTag: "zero" ) << qfloat16(0) << FP_ZERO; |
409 | QTest::newRow(dataTag: "-zero" ) << -qfloat16(0) << FP_ZERO; |
410 | QTest::newRow(dataTag: "one" ) << qfloat16(1) << FP_NORMAL; |
411 | QTest::newRow(dataTag: "-one" ) << qfloat16(-1) << FP_NORMAL; |
412 | QTest::newRow(dataTag: "ten" ) << qfloat16(10) << FP_NORMAL; |
413 | QTest::newRow(dataTag: "-ten" ) << qfloat16(-10) << FP_NORMAL; |
414 | QTest::newRow(dataTag: "max" ) << Bounds::max() << FP_NORMAL; |
415 | QTest::newRow(dataTag: "lowest" ) << Bounds::lowest() << FP_NORMAL; |
416 | QTest::newRow(dataTag: "min" ) << Bounds::min() << FP_NORMAL; |
417 | QTest::newRow(dataTag: "-min" ) << -Bounds::min() << FP_NORMAL; |
418 | QTest::newRow(dataTag: "denorm_min" ) << Bounds::denorm_min() << FP_SUBNORMAL; |
419 | QTest::newRow(dataTag: "-denorm_min" ) << -Bounds::denorm_min() << FP_SUBNORMAL; |
420 | } |
421 | |
422 | void tst_qfloat16::finite() |
423 | { |
424 | QFETCH(qfloat16, value); |
425 | QFETCH(int, mode); |
426 | QCOMPARE(value.isNormal(), mode == FP_NORMAL); |
427 | QCOMPARE(value, value); // Fuzzy |
428 | QVERIFY(value == value); // Exact |
429 | QVERIFY(qIsFinite(value)); |
430 | QVERIFY(!qIsInf(value)); |
431 | QVERIFY(!qIsNaN(value)); |
432 | QCOMPARE(qFpClassify(value), mode); |
433 | |
434 | // *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy |
435 | // comparison, and we need exact here. |
436 | const qfloat16 zero(0), plus(+1), minus(-1); |
437 | const qfloat16 magnitude = (value < zero) ? -value : value; |
438 | QVERIFY(value.copySign(plus) == magnitude); |
439 | QVERIFY(value.copySign(minus) == -magnitude); |
440 | } |
441 | |
442 | void tst_qfloat16::properties() |
443 | { |
444 | using Bounds = std::numeric_limits<qfloat16>; |
445 | QVERIFY(Bounds::is_specialized); |
446 | QVERIFY(Bounds::is_signed); |
447 | QVERIFY(!Bounds::is_integer); |
448 | QVERIFY(!Bounds::is_exact); |
449 | QVERIFY(Bounds::is_iec559); |
450 | QVERIFY(Bounds::is_bounded); |
451 | QVERIFY(!Bounds::is_modulo); |
452 | QVERIFY(!Bounds::traps); |
453 | QVERIFY(Bounds::has_infinity); |
454 | QVERIFY(Bounds::has_quiet_NaN); |
455 | QVERIFY(Bounds::has_signaling_NaN); |
456 | QCOMPARE(Bounds::has_denorm, std::denorm_present); |
457 | QCOMPARE(Bounds::round_style, std::round_to_nearest); |
458 | QCOMPARE(Bounds::radix, 2); |
459 | // Untested: has_denorm_loss |
460 | } |
461 | |
462 | void tst_qfloat16::limits() // See also: qNaN() and infinity() |
463 | { |
464 | // *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy |
465 | // comparison, and we need exact here. |
466 | using Bounds = std::numeric_limits<qfloat16>; |
467 | |
468 | // A few useful values: |
469 | const qfloat16 zero(0), one(1), ten(10); |
470 | |
471 | // The specifics of minus zero: |
472 | // (IEEE 754 seems to want -zero < zero, but -0. == 0. and -0.f == 0.f in C++.) |
473 | QVERIFY(-zero <= zero); |
474 | QVERIFY(-zero == zero); |
475 | QVERIFY(!(-zero > zero)); |
476 | |
477 | // digits in the mantissa, including the implicit 1 before the binary dot at its left: |
478 | QVERIFY(qfloat16(1 << (Bounds::digits - 1)) + one > qfloat16(1 << (Bounds::digits - 1))); |
479 | QVERIFY(qfloat16(1 << Bounds::digits) + one == qfloat16(1 << Bounds::digits)); |
480 | |
481 | // There is a wilful of-by-one in how m(ax|in)_exponent are defined; they're |
482 | // the lowest and highest n for which radix^{n-1} are normal and finite. |
483 | const qfloat16 two(Bounds::radix); |
484 | qfloat16 bit = powf16(base: two, raise: Bounds::max_exponent - 1); |
485 | QVERIFY(qIsFinite(bit)); |
486 | QVERIFY(qIsInf(bit * two)); |
487 | bit = powf16(base: two, raise: Bounds::min_exponent - 1); |
488 | QVERIFY(bit.isNormal()); |
489 | QCOMPARE(qFpClassify(bit), FP_NORMAL); |
490 | QVERIFY(!(bit / two).isNormal()); |
491 | QCOMPARE(qFpClassify(bit / two), FP_SUBNORMAL); |
492 | QVERIFY(bit / two > zero); |
493 | |
494 | // Base ten (with no matching off-by-one idiocy): |
495 | // the lowest negative number n such that 10^n is a valid normalized value |
496 | qfloat16 low10(powf16(base: ten, raise: Bounds::min_exponent10)); |
497 | QVERIFY(low10 > zero); |
498 | QVERIFY(low10.isNormal()); |
499 | low10 /= ten; |
500 | QVERIFY(low10 == zero || !low10.isNormal()); |
501 | // the largest positive number n such that 10^n is a representable finite value |
502 | qfloat16 high10(powf16(base: ten, raise: Bounds::max_exponent10)); |
503 | QVERIFY(high10 > zero); |
504 | QVERIFY(qIsFinite(high10)); |
505 | QVERIFY(!qIsFinite(high10 * ten)); |
506 | QCOMPARE(qFpClassify(high10), FP_NORMAL); |
507 | |
508 | // How many digits are significant ? (Casts avoid linker errors ...) |
509 | QCOMPARE(int(Bounds::digits10), 3); // 9.79e-4 has enough sigificant digits: |
510 | qfloat16 below(9.785e-4f), above(9.794e-4f); |
511 | #if 0 // Sadly, the QEMU x-compile for arm64 "optimizes" comparisons: |
512 | const bool overOptimized = false; |
513 | #else |
514 | const bool overOptimized = (below != above); |
515 | if (overOptimized) |
516 | QEXPECT_FAIL("" , "Over-optimized on ARM" , Continue); |
517 | #endif // (but it did, so should, pass everywhere else, confirming digits10 is indeed 3). |
518 | QVERIFY(below == above); |
519 | QCOMPARE(int(Bounds::max_digits10), 5); // we need 5 to distinguish these two: |
520 | QVERIFY(qfloat16(1000.5f) != qfloat16(1001.4f)); |
521 | |
522 | // Actual limiting values of the type: |
523 | const qfloat16 rose(one + Bounds::epsilon()); |
524 | QVERIFY(rose > one); |
525 | if (overOptimized) |
526 | QEXPECT_FAIL("" , "Over-optimized on ARM" , Continue); |
527 | QVERIFY(one + Bounds::epsilon() / rose == one); |
528 | |
529 | QVERIFY(Bounds::max() > zero); |
530 | QVERIFY(qIsInf(Bounds::max() * rose)); |
531 | |
532 | QVERIFY(Bounds::lowest() < zero); |
533 | QVERIFY(qIsInf(Bounds::lowest() * rose)); |
534 | |
535 | QVERIFY(Bounds::min() > zero); |
536 | QVERIFY(!(Bounds::min() / rose).isNormal()); |
537 | |
538 | QVERIFY(Bounds::denorm_min() > zero); |
539 | if (overOptimized) |
540 | QEXPECT_FAIL("" , "Over-optimized on ARM" , Continue); |
541 | QVERIFY(Bounds::denorm_min() / rose == zero); |
542 | if (overOptimized) |
543 | QEXPECT_FAIL("" , "Over-optimized on ARM" , Continue); |
544 | const qfloat16 under = (-Bounds::denorm_min()) / rose; |
545 | QVERIFY(under == -zero); |
546 | QCOMPARE(qfloat16(1).copySign(under), qfloat16(-1)); |
547 | } |
548 | |
549 | QTEST_APPLESS_MAIN(tst_qfloat16) |
550 | #include "tst_qfloat16.moc" |
551 | |