summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/global/qfloat16
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2019-05-28 16:41:49 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2019-06-03 15:14:42 +0200
commite4079eca49adce16e31dac2a18d49d7a55817891 (patch)
tree1dfb960ec1115b1f552afe8a013058542389505e /tests/auto/corelib/global/qfloat16
parentf32a6cfb6b6236533508901f114ab57396da8ff3 (diff)
parentec6dc5f78453048c4f0604655a34c6c20c79d819 (diff)
Merge remote-tracking branch 'origin/dev' into wip/cmake
Diffstat (limited to 'tests/auto/corelib/global/qfloat16')
-rw-r--r--tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp165
1 files changed, 164 insertions, 1 deletions
diff --git a/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp b/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp
index 241dccb90e..0848a4160f 100644
--- a/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp
+++ b/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2016 by Southwest Research Institute (R)
** Contact: https://www.qt.io/licensing/
**
@@ -48,6 +49,7 @@ private slots:
void arithOps();
void floatToFloat16();
void floatFromFloat16();
+ void limits();
};
void tst_qfloat16::fuzzyCompare_data()
@@ -172,7 +174,9 @@ void tst_qfloat16::qNan()
QVERIFY(qIsInf(-inf));
QVERIFY(qIsInf(2.f*inf));
QVERIFY(qIsInf(inf*2.f));
- QCOMPARE(qfloat16(1.f/inf), qfloat16(0.f));
+ // QTBUG-75812: QEMU's over-optimized arm64 flakily fails 1/inf == 0 :-(
+ if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f))
+ QCOMPARE(qfloat16(1.f/inf), qfloat16(0.f));
#ifdef Q_CC_INTEL
QEXPECT_FAIL("", "ICC optimizes zero * anything to zero", Continue);
#endif
@@ -345,5 +349,164 @@ void tst_qfloat16::floatFromFloat16()
QCOMPARE(out[i], expected[i]);
}
+static qfloat16 powf16(qfloat16 base, int raise)
+{
+ const qfloat16 one(1.f);
+ if (raise < 0) {
+ raise = -raise;
+ base = one / base;
+ }
+ qfloat16 answer = (raise & 1) ? base : one;
+ while (raise > 0) {
+ raise >>= 1;
+ base *= base;
+ if (raise & 1)
+ answer *= base;
+ }
+ return answer;
+}
+
+void tst_qfloat16::limits()
+{
+ // *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy
+ // comparison, and we need exact here.
+ using Bounds = std::numeric_limits<qfloat16>;
+ QVERIFY(Bounds::is_specialized);
+ QVERIFY(Bounds::is_signed);
+ QVERIFY(!Bounds::is_integer);
+ QVERIFY(!Bounds::is_exact);
+ QVERIFY(Bounds::is_iec559);
+ QVERIFY(Bounds::is_bounded);
+ QVERIFY(!Bounds::is_modulo);
+ QVERIFY(!Bounds::traps);
+ QVERIFY(Bounds::has_infinity);
+ QVERIFY(Bounds::has_quiet_NaN);
+ QVERIFY(Bounds::has_signaling_NaN);
+ QCOMPARE(Bounds::has_denorm, std::denorm_present);
+ QCOMPARE(Bounds::round_style, std::round_to_nearest);
+ QCOMPARE(Bounds::radix, 2);
+ // Untested: has_denorm_loss
+
+ // A few common values:
+ const qfloat16 zero(0), one(1), ten(10);
+ QVERIFY(qIsFinite(zero));
+ QVERIFY(!qIsInf(zero));
+ QVERIFY(!qIsNaN(zero));
+ QCOMPARE(qFpClassify(zero), FP_ZERO);
+ QVERIFY(qIsFinite(one));
+ QVERIFY(!qIsInf(one));
+ QCOMPARE(qFpClassify(one), FP_NORMAL);
+ QVERIFY(!qIsNaN(one));
+ QVERIFY(qIsFinite(ten));
+ QVERIFY(!qIsInf(ten));
+ QVERIFY(!qIsNaN(ten));
+ QCOMPARE(qFpClassify(ten), FP_NORMAL);
+
+ // digits in the mantissa, including the implicit 1 before the binary dot at its left:
+ QVERIFY(qfloat16(1 << (Bounds::digits - 1)) + one > qfloat16(1 << (Bounds::digits - 1)));
+ QVERIFY(qfloat16(1 << Bounds::digits) + one == qfloat16(1 << Bounds::digits));
+
+ // There is a wilful of-by-one in how m(ax|in)_exponent are defined; they're
+ // the lowest and highest n for which radix^{n-1} are normal and finite.
+ const qfloat16 two(Bounds::radix);
+ qfloat16 bit = powf16(two, Bounds::max_exponent - 1);
+ QVERIFY(qIsFinite(bit));
+ QVERIFY(qIsInf(bit * two));
+ bit = powf16(two, Bounds::min_exponent - 1);
+ QVERIFY(bit.isNormal());
+ QCOMPARE(qFpClassify(bit), FP_NORMAL);
+ QVERIFY(!(bit / two).isNormal());
+ QCOMPARE(qFpClassify(bit / two), FP_SUBNORMAL);
+ QVERIFY(bit / two > zero);
+
+ // Base ten (with no matching off-by-one idiocy):
+ // the lowest negative number n such that 10^n is a valid normalized value
+ qfloat16 low10(powf16(ten, Bounds::min_exponent10));
+ QVERIFY(low10 > zero);
+ QVERIFY(low10.isNormal());
+ low10 /= ten;
+ QVERIFY(low10 == zero || !low10.isNormal());
+ // the largest positive number n such that 10^n is a representable finite value
+ qfloat16 high10(powf16(ten, Bounds::max_exponent10));
+ QVERIFY(high10 > zero);
+ QVERIFY(qIsFinite(high10));
+ QVERIFY(!qIsFinite(high10 * ten));
+ QCOMPARE(qFpClassify(high10), FP_NORMAL);
+
+ // How many digits are significant ? (Casts avoid linker errors ...)
+ QCOMPARE(int(Bounds::digits10), 3); // 9.79e-4 has enough sigificant digits:
+ qfloat16 below(9.785e-4f), above(9.794e-4f);
+#if 0 // Sadly, the QEMU x-compile for arm64 "optimises" comparisons:
+ const bool overOptimised = false;
+#else
+ const bool overOptimised = (below != above);
+ if (overOptimised)
+ QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
+#endif // (but it did, so should, pass everywhere else, confirming digits10 is indeed 3).
+ QVERIFY(below == above);
+ QCOMPARE(int(Bounds::max_digits10), 5); // we need 5 to distinguish these two:
+ QVERIFY(qfloat16(1000.5f) != qfloat16(1001.4f));
+
+ // Actual limiting values of the type:
+ const qfloat16 rose(one + Bounds::epsilon());
+ QVERIFY(rose > one);
+ if (overOptimised)
+ QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
+ QVERIFY(one + Bounds::epsilon() / rose == one);
+ QVERIFY(qIsInf(Bounds::infinity()));
+ QVERIFY(!qIsNaN(Bounds::infinity()));
+ QVERIFY(!qIsFinite(Bounds::infinity()));
+ QCOMPARE(Bounds::infinity(), Bounds::infinity());
+ QCOMPARE(qFpClassify(Bounds::infinity()), FP_INFINITE);
+
+ QVERIFY(Bounds::infinity() > -Bounds::infinity());
+ QVERIFY(Bounds::infinity() > zero);
+ QVERIFY(qIsInf(-Bounds::infinity()));
+ QVERIFY(!qIsNaN(-Bounds::infinity()));
+ QVERIFY(!qIsFinite(-Bounds::infinity()));
+ QCOMPARE(-Bounds::infinity(), -Bounds::infinity());
+ QCOMPARE(qFpClassify(-Bounds::infinity()), FP_INFINITE);
+
+ QVERIFY(-Bounds::infinity() < zero);
+ QVERIFY(qIsNaN(Bounds::quiet_NaN()));
+ QVERIFY(!qIsInf(Bounds::quiet_NaN()));
+ QVERIFY(!qIsFinite(Bounds::quiet_NaN()));
+ QVERIFY(!(Bounds::quiet_NaN() == Bounds::quiet_NaN()));
+ QCOMPARE(Bounds::quiet_NaN(), Bounds::quiet_NaN());
+ QCOMPARE(qFpClassify(Bounds::quiet_NaN()), FP_NAN);
+
+ QVERIFY(Bounds::max() > zero);
+ QVERIFY(qIsFinite(Bounds::max()));
+ QVERIFY(!qIsInf(Bounds::max()));
+ QVERIFY(!qIsNaN(Bounds::max()));
+ QVERIFY(qIsInf(Bounds::max() * rose));
+ QCOMPARE(qFpClassify(Bounds::max()), FP_NORMAL);
+
+ QVERIFY(Bounds::lowest() < zero);
+ QVERIFY(qIsFinite(Bounds::lowest()));
+ QVERIFY(!qIsInf(Bounds::lowest()));
+ QVERIFY(!qIsNaN(Bounds::lowest()));
+ QVERIFY(qIsInf(Bounds::lowest() * rose));
+ QCOMPARE(qFpClassify(Bounds::lowest()), FP_NORMAL);
+
+ QVERIFY(Bounds::min() > zero);
+ QVERIFY(Bounds::min().isNormal());
+ QVERIFY(!(Bounds::min() / rose).isNormal());
+ QVERIFY(qIsFinite(Bounds::min()));
+ QVERIFY(!qIsInf(Bounds::min()));
+ QVERIFY(!qIsNaN(Bounds::min()));
+ QCOMPARE(qFpClassify(Bounds::min()), FP_NORMAL);
+
+ QVERIFY(Bounds::denorm_min() > zero);
+ QVERIFY(!Bounds::denorm_min().isNormal());
+ QVERIFY(qIsFinite(Bounds::denorm_min()));
+ QVERIFY(!qIsInf(Bounds::denorm_min()));
+ QVERIFY(!qIsNaN(Bounds::denorm_min()));
+ if (overOptimised)
+ QEXPECT_FAIL("", "Over-optimised on QEMU", Continue);
+ QCOMPARE(Bounds::denorm_min() / rose, zero);
+ QCOMPARE(qFpClassify(Bounds::denorm_min()), FP_SUBNORMAL);
+}
+
QTEST_APPLESS_MAIN(tst_qfloat16)
#include "tst_qfloat16.moc"