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