diff options
Diffstat (limited to 'tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp')
-rw-r--r-- | tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp | 230 |
1 files changed, 210 insertions, 20 deletions
diff --git a/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp b/tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp index b73a297245..94f0afa5ed 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/ ** @@ -40,7 +41,8 @@ private slots: void fuzzyCompare(); void ltgt_data(); void ltgt(); - void qNan(); + void qNaN(); + void infinity(); void float_cast(); void float_cast_data(); void promotionTests(); @@ -48,6 +50,10 @@ private slots: void arithOps(); void floatToFloat16(); void floatFromFloat16(); + void finite_data(); + void finite(); + void properties(); + void limits(); }; void tst_qfloat16::fuzzyCompare_data() @@ -101,6 +107,7 @@ void tst_qfloat16::ltgt_data() QTest::addColumn<float>("val2"); QTest::newRow("zero") << 0.0f << 0.0f; + QTest::newRow("-zero") << -0.0f << 0.0f; QTest::newRow("ten") << 10.0f << 10.0f; QTest::newRow("large") << 100000.0f << 100000.0f; QTest::newRow("small") << 0.0000001f << 0.0000001f; @@ -152,38 +159,66 @@ void tst_qfloat16::ltgt() # pragma GCC optimize "no-fast-math" #endif -void tst_qfloat16::qNan() +void tst_qfloat16::qNaN() { #if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404) QSKIP("Non-conformant fast math mode is enabled, cannot run test"); #endif - qfloat16 nan = qQNaN(); - QVERIFY(!(0. > nan)); - QVERIFY(!(0. < nan)); + using Bounds = std::numeric_limits<qfloat16>; + const qfloat16 nan = Bounds::quiet_NaN(); + const qfloat16 zero(0), one(1); + QVERIFY(!(zero > nan)); + QVERIFY(!(zero < nan)); + QVERIFY(!(zero == nan)); QVERIFY(!qIsInf(nan)); QVERIFY(qIsNaN(nan)); - QVERIFY(qIsNaN(nan + 1.f)); + QVERIFY(qIsNaN(nan + one)); QVERIFY(qIsNaN(-nan)); - qfloat16 inf = qInf(); - QVERIFY(inf > qfloat16(0)); - QVERIFY(-inf < qfloat16(0)); - QVERIFY(!qIsNaN(inf)); - QVERIFY(qIsInf(inf)); - QVERIFY(qIsInf(-inf)); - QVERIFY(qIsInf(2.f*inf)); - QVERIFY(qIsInf(inf*2.f)); - // QTBUG-75812: QEMU/arm64 compiler over-optimizes, so 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 - QVERIFY(qIsNaN(nan*0.f)); + QVERIFY(qIsNaN(nan * zero)); #ifdef Q_CC_INTEL QEXPECT_FAIL("", "ICC optimizes zero * anything to zero", Continue); #endif - QVERIFY(qIsNaN(inf*0.f)); - QVERIFY(qFuzzyCompare(qfloat16(1.f/inf), qfloat16(0.0))); + QVERIFY(qIsNaN(Bounds::infinity() * zero)); + + QVERIFY(!nan.isNormal()); + QVERIFY(!qIsFinite(nan)); + QVERIFY(!(nan == nan)); + QCOMPARE(nan, nan); // Despite the preceding + QCOMPARE(qFpClassify(nan), FP_NAN); +} + +void tst_qfloat16::infinity() +{ + const qfloat16 huge = std::numeric_limits<qfloat16>::infinity(); + const qfloat16 zero(0), one(1), two(2); + QVERIFY(huge > -huge); + QVERIFY(huge > zero); + QVERIFY(-huge < zero); + QCOMPARE(huge, huge); + QCOMPARE(-huge, -huge); + + // QTBUG-75812 - see overOptimized in the limits() test. + if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f)) { + QCOMPARE(one / huge, zero); + QVERIFY(qFuzzyCompare(one / huge, zero)); // (same thing) + } + + QVERIFY(qIsInf(huge)); + QVERIFY(qIsInf(-huge)); + QVERIFY(qIsInf(two * huge)); + QVERIFY(qIsInf(huge * two)); + + QVERIFY(!huge.isNormal()); + QVERIFY(!(-huge).isNormal()); + QVERIFY(!qIsNaN(huge)); + QVERIFY(!qIsNaN(-huge)); + QVERIFY(!qIsFinite(huge)); + QVERIFY(!qIsFinite(-huge)); + QCOMPARE(qFpClassify(huge), FP_INFINITE); + QCOMPARE(qFpClassify(-huge), FP_INFINITE); } void tst_qfloat16::float_cast_data() @@ -347,5 +382,160 @@ 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::finite_data() +{ + using Bounds = std::numeric_limits<qfloat16>; + QTest::addColumn<qfloat16>("value"); + QTest::addColumn<int>("mode"); + + QTest::newRow("zero") << qfloat16(0) << FP_ZERO; + QTest::newRow("-zero") << -qfloat16(0) << FP_ZERO; + QTest::newRow("one") << qfloat16(1) << FP_NORMAL; + QTest::newRow("-one") << qfloat16(-1) << FP_NORMAL; + QTest::newRow("ten") << qfloat16(10) << FP_NORMAL; + QTest::newRow("-ten") << qfloat16(-10) << FP_NORMAL; + QTest::newRow("max") << Bounds::max() << FP_NORMAL; + QTest::newRow("lowest") << Bounds::lowest() << FP_NORMAL; + QTest::newRow("min") << Bounds::min() << FP_NORMAL; + QTest::newRow("-min") << -Bounds::min() << FP_NORMAL; + QTest::newRow("denorm_min") << Bounds::denorm_min() << FP_SUBNORMAL; + QTest::newRow("-denorm_min") << -Bounds::denorm_min() << FP_SUBNORMAL; +} + +void tst_qfloat16::finite() +{ + QFETCH(qfloat16, value); + QFETCH(int, mode); + QCOMPARE(value.isNormal(), mode != FP_SUBNORMAL); + QCOMPARE(value, value); // Fuzzy + QVERIFY(value == value); // Exact + QVERIFY(qIsFinite(value)); + QVERIFY(!qIsInf(value)); + QVERIFY(!qIsNaN(value)); + QCOMPARE(qFpClassify(value), mode); +} + +void tst_qfloat16::properties() +{ + 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 +} + +void tst_qfloat16::limits() // See also: qNaN() and infinity() +{ + // *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy + // comparison, and we need exact here. + using Bounds = std::numeric_limits<qfloat16>; + + // A few useful values: + const qfloat16 zero(0), one(1), ten(10); + + // The specifics of minus zero: + // (IEEE 754 seems to want -zero < zero, but -0. == 0. and -0.f == 0.f in C++.) + QVERIFY(-zero <= zero); + QVERIFY(-zero == zero); + QVERIFY(!(-zero > zero)); + + // 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 "optimizes" comparisons: + const bool overOptimized = false; +#else + const bool overOptimized = (below != above); + if (overOptimized) + QEXPECT_FAIL("", "Over-optimized on ARM", 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 (overOptimized) + QEXPECT_FAIL("", "Over-optimized on ARM", Continue); + QVERIFY(one + Bounds::epsilon() / rose == one); + + QVERIFY(Bounds::max() > zero); + QVERIFY(qIsInf(Bounds::max() * rose)); + + QVERIFY(Bounds::lowest() < zero); + QVERIFY(qIsInf(Bounds::lowest() * rose)); + + QVERIFY(Bounds::min() > zero); + QVERIFY(!(Bounds::min() / rose).isNormal()); + + QVERIFY(Bounds::denorm_min() > zero); + if (overOptimized) + QEXPECT_FAIL("", "Over-optimized on ARM", Continue); + QVERIFY(Bounds::denorm_min() / rose == zero); + if (overOptimized) + QEXPECT_FAIL("", "Over-optimized on ARM", Continue); + QVERIFY(-Bounds::denorm_min() / rose == -zero); +} + QTEST_APPLESS_MAIN(tst_qfloat16) #include "tst_qfloat16.moc" |